Linux 模块

Linux 模块

什么是 Linux 模块

Linux模块,也就是可加载内核模块(LKMs),允许在运行时动态加载到内核中。

这说明两点:

是内核模块, 也就是说是内核的一部分, 只能依赖内核的接口, 且必须遵循内核的规则.

运行时可加载, 这避免了重复编译内核和重启系统. 并且内核和模块是分开的, 部署更灵活. 同时也可以只在需要的时候加载模块, 节省内核的内存占用.

开发模块时常用的命令

insmod module.ko # 加载模块

rmmod module.ko # 卸载模块

modprobe module.ko # 智能加载模块, 会自动解决依赖

lsmod # 查看已加载的模块

modinfo module.ko # 查看模块信息

depmod -a # 更新模块依赖

dmesg # 查看内核日志

uname -r # 查看内核版本

如何实现一个 Linux 模块

编写模块代码

#include

#include

// 不能依赖c库, 只能使用内核提供的接口

MODULE_LICENSE("Dual BSD/GPL"); // 必须有,否则报错. 模块许可证,许可可选

static int __init myinit(void) // 必须有,在模块加载时调用

{

printk("Hi module!\n");

return 0;

}

static void __exit myexit(void) // 必须有,在模块卸载时调用

{

printk("Bye module!\n");

}

module_init(myinit); // 必须有,指定 这是模块加载时调用的函数

module_exit(myexit); // 必须有,指定 模块卸载时调用的函数

模块参数

在模块代码中加入如下代码:

#include

int param = 0;

// module_param(变量名, 类型, 权限)

// 类型可以是byte, hexint, short, ushort, int, uint, long, ulong, charp, bool, invbool

// module参数会在/sys/module/模块名/parameters/下生成一个文件, 权限就是设置这个文件的权限, 管理谁可以读取修改这个参数.

module_param(param, int, S_IRUGO);

int arrParam[3];

int arrNum = 3; // 有两作用, 1. 初始值用来限制用户传入的数组个数 2. 会被更改为实际传入的数组个数

module_param_array(arrParam, int, &arrSize, S_IRUGO); // 传入数组

在加载模块时可以传入参数, 例如insmod module.ko param=1 arrParam=1,2,3

权限相关的宏

说明

S_IRUSR

所有者可读

S_IRGRP

组可读

S_IROTH

其他可读

S_IWUSR

owner可写

S_IRWXUGO

所有者,组,其他可读写执行

...

格式就是S_I[RWX][UGO]

编写 Makefile

obj-m := module.o // 表明有一个模块要从module.o建立

module-objs := file1.o file2.o // 表明module.o的依赖, file1.o会自动根据名字找到对应的源文件file1.c(其他后缀的情况我不清楚)生成. 如果一个模块只由一个源文件生成, 可以直接写成obj-m := file1.o

#generate the path

CURRENT_PATH:=$(shell pwd)

#the current kernel version number

LINUX_KERNEL:=$(shell uname -r)

#the absolute path

LINUX_KERNEL_PATH:=/usr/src/linux-headers-$(LINUX_KERNEL)

#complie object

all:

make -C $(LINUXKERNELPATH) M=$(CURRENT_PATH) modules

# -C 改变目录到指定内核目录, M= 指定模块所在目录

#clean

clean:

make -C $(LINUXKERNELPATH) M=$(CURRENT_PATH) clean

编译模块有几个前提:

编译模块实际使用的是内核的Makefile, 所以必须要先下载对应的内核代码并编译成功.

保证你有版本足够新的编译器, 模块工具, 以及其他必要工具. 在内核Documentation/Changes 列出了需要的工具版本

加载/卸载模块

insmod module.ko

rmmod module.ko

modprobe module.ko # 与insmod的区别是如果要加载的模块引用了内核中为定义的符号,modprobe会在当前模块搜索路径中寻找其他模块

Linux 模块是如何工作的

https://www.cnblogs.com/fanzhidongyzby/p/3730131.html

.ko文件是什么

本质是一个ELF文件, 和.o文件差不多,都是REL(relocatable file), 只是多了一些元数据,比如模块许可证, 然后把多个.o打包成一个.ko, 还多了导出的符号表.

模块加载的过程

模块加载时会使用一个系统调用sys_init_module, 在内核代码中使用SYSCALL_DEFINE3(init_module,...)来定义这个系统调用.

主要代码是

err = copy_module_from_user(umod, len, &info);

...

return load_module(&info, uargs, 0);

copy_module_from_user是把用户空间的模块数据拷贝到内核空间, 这部分内容需要理解内存管理机制.最终会把模块数据放到info中.

load_module涉及到几个部分, ELF解析,内存管理等, 暂时不深入.

怎么把用户空间数据copy到内核空间

补充: 内核模块和用户程序的区别

只连接到内核, 能够调用的唯一的函数是内核输出的那些.

错误处理

用户空间和内核空间的不同

内核的并发, 所以内核代码必须是可重入的--能在多个上下文中同时运行

应用程序存在于虚拟内存中, 有一个非常大的堆栈区. 内核, 相反, 有一

个非常小的堆栈,并和所有内核空间调用链共享;

小心使用__开始的函数, 如果你调用这个函数, 确信你知道你在做什么.

内核代码不能做浮点运算, 使能浮点将要求内核在每次进出内核空间的时候保存

和恢复浮点处理器的状态, 增加了不必要的开销.

linux 内核头文件提供了方便来管理你的符号的可见性, 如果需要输出符号给其他模块使用需要使用EXPORT_SYMBOL()/EXPORT_SYMBOL_GPL()宏._GPL表示只给GPL许可证的模块使用.

相关手记

365体育投注网站 地磅干扰器

地磅干扰器

07-12 👁️ 8771
365bet投注网 pc端大型单机游戏有哪些
365bet线上网址 一切都是浮云的说说 除了生死一切都是浮云说说(精选2篇)
365bet线上网址 手机分期需要多久

手机分期需要多久

07-17 👁️ 1929
365体育投注网站 桌面日记本软件App哪个好 桌面日记本
365bet投注网 想要买到靠谱的大牌护肤品小样,应该从哪里买呢?