内核模块 kernel module
简介
内核模块,可以根据需要去加载进内核或者从内核中卸载的代码块,这操作期间可以不用重启系统。这样使得内核镜像可以按要求裁剪,不用全部功能都去加载进去导致镜像超级大 累赘,而且没有内核模块的方便加载卸载功能,每次添加内核功能,就需要重新编译烧写镜像
module加载卸载
内核模块查看:lsmod,通过读/sys/module目录下的文件获取已经加载进内核的所有模块信息,
模块加载:modprobe 模块名
内核需要加载一个模块时,内核模块daemon程序kmod会执行modprobe将相应模块加载进内核
模块名:cat /etc/modeprobe.d/aliases
知道模块名之后,modprobe会去读/lib/modules/
v
e
r
s
i
o
n
/
m
o
d
u
l
e
s
.
d
e
p
,
v
e
r
s
i
o
n
是
内
核
版
本
号
,
可
以
使
用
u
n
a
m
e
−
r
得
到
,
m
o
d
u
l
e
s
.
d
e
p
是
d
e
m
o
d
−
a
命
令
生
成
,
是
将
/
l
i
b
/
m
o
d
u
l
e
s
/
version/modules.dep,version是内核版本号,可以使用uname -r得到,modules.dep是demod -a命令生成,是将/lib/modules/
version/modules.dep,version是内核版本号,可以使用uname−r得到,modules.dep是demod−a命令生成,是将/lib/modules/version下所有模块的依赖关系生成到modules.dep中。
然后,modprobe会根据modules.dep中的依赖关系,调用insmod将需要的模块按依赖关系加载
总结:
加载一个module ko的步骤:
1、将ko文件拷贝到/lib/modules/$version目录下
2、调用命令demod -a生成modules.dep
3、调用modprobe 模块名
也可以直接使用insmod +ko文件,但是需要自己处理模块的依赖关系
模块卸载:modpeobe -r 模块名 或者 rmmod 模块名
卸载删除模块时,modprobe和rmmod会去dao/lib/modules/$version下找相应的依赖关系和其他信息,若没有这个目录,卸载删除模块报错
module编写框架
这里先了解一下内核模块和一般应用程序的区别:
1、一般的应用程序,从头到尾执行单个任务,然后到main就立即结束了;设备驱动是预先注册自己以便来服务将来的某个请求,然后它的初始化函数就结束了,就是模块初始化函数的任务就是为以后调用模块函数做准备
2、一般的应用程序在退出时,可以不用管资源的释放或者其他的清除工作;模块的退出函数必须撤销初始化函数做的一切,否则在系统引导之前某些东西就会残留在系统中
3、一般的应用程序可使用从外部引用的函数库,可以调用它自己未定义的函数,因为连接过程中就从那些库中解析到;模块驱动仅仅被链接到内核,它能够调用的函数仅仅是由内核导出的那些函数,不存在任何可连接的函数库
4、一般的应用程序在开发过程中的段错误是无害的,还可以跟踪到源码问题位置;内核错误即使不影响整个系统,也至少会杀死当前的进程,
模块的入口和退出
模块初始化函数init_module();模块退出清理函数cleanup_module()
#include <linux/module.h>
#include <linux/kernel.h>
#ifdef MODULE
MODULE_LICENSE("GPL");
int init_module(void)
{
printk (KERNEL_INFO, "hello!\n");
}
void cleanup_module(void)
{
printk (KERNEL_INFO, "bye\n");
}
#endif
一般的,init_module或者注册一个handle到内核,或者用自己代码取代内核功能函数;cleanup_module做模块清理工作,释放模块申请的资源。
从内核2.3.13版本开始,可以不用一定使用这两个函数,而可以使用自己定义的函数作为模块的初始化和退出函数,但是要符合init_module和cleanup_module的原型,并且在代码中要加入下面语句:
module_init(hello_init);
module_exit(hello_exit);
模块的编译
编写makefile
obj-m += hello.o
all:
make -C /lib/modules/$(shell uname -r)/build M = $(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M = $(PWD) clean
详细内核编译问题可以查看内核提供的kbuild机制文档:
linux/Documentation/kbuild/modules.rst
2.1 Command Syntax
==================
The command to build an external module is::
$ make -C <path_to_kernel_src> M=$PWD
The kbuild system knows that an external module is being built
due to the "M=<dir>" option given in the command.
To build against the running kernel use::
$ make -C /lib/modules/`uname -r`/build M=$PWD
Then to install the module(s) just built, add the target
"modules_install" to the command::
$ make -C /lib/modules/`uname -r`/build M=$PWD modules_install
License
内核是开源的,要在kernel中写代码,要遵循它的license,linux内核提供一个声明
MODULE_LICENDE ("GPL");
这个声明表示我们的代码遵循GPL(GNU General Public License)协议,其他的一些协议在<linux/module.h>中
模块参数
可以通过命令行来给模块传递初始值的全局变量
1、module_param(name, type, perm)
name:接收参数值的全局变量名
type:变量的类型
perm:参数的权限,和文件权限一般
int gVariable = 1;
module_param (gVariable, int, 0660);
insmod module.ko gVariable = 100
2、module_param_array (name, type, nump, perm)
数组类型的模块全局变量
nump:一个指针,指向的变量存放用户实际传递数组元素的个数,可以设为NULL
3、module_param_string(name, string, len, perm)
string:字符数组名
要描述一个模块参数,可以使用宏(linux/module.h):
MODULE_PARM_DESC (name, desc)
int gVariable = 1;
module_param (gVariable, int, 0660);
MODULE_PARM_DESC (gVariable, "global variable");
insmod module.ko gVariable = 100