内核模块可以在不重新编译内核的情况下添加到内核代码中运行,其可以动态加载和卸载,因此可以根据需要将内核某些功能独立出来作为模块,而不是编译到内核中,这样可以减少内核大小,并且可以按照实际需求选择裁剪或添加某些内核功能。
1. 编写一个内核模块
需要先强调一个最基本的知识,内核模块是要运行在内核态的代码,所以编写内核模块需要包含的头文件都是内核中的头文件,使用的函数都是内核的函数。
编写一个内核模块首先要确保内核打开了CONFIG_MODULES宏,并且已经编译了内核。
需要添加头文件<linux/module.h>
编写内核模块必须要实现一个init方法和一个exit方法。由于模块是可以在内核运行过程中动态的加载和卸载的,因此,在加载模块时要指定一个init方法作为入口函数,同样的,在卸载模块的时候,exit方法可以做一些清理工作。
一个最简单的内核模块的实现如下(testm.c):
#include <linux/module.h>
static int __init xxx_init(void)
{
/* TODO: 在这个模块中你想做的事情 */
return 0;
}
static void __exit xxx_exit(void)
{
/* TODO: 卸载这个模块时需要做的清理工作 */
}
module_init(xxx_init);
module_exit(xxx_exit);
定义了init和exit方法后,要使用module_init宏来注册init方法,module_exit宏来注册exit方法。
Makefile的内容很简单:
obj-m += testm.o
2. 编译内核模块
写好代码后进行编译,编译内核模块的目的是生成一个.ko文件,加载模块就是加载这个.ko文件到内核中。2.1 基于宿主机上正在运行的内核进行编译
[root@zhenfg workshop]# make -C /lib/modules/`uname -r`/build M=$PWD
make: Entering directory `/usr/src/kernels/2.6.18-8.el5-i686'
LD /home/ndisk/workshop/built-in.o
CC [M] /home/ndisk/workshop/testm.o
Building modules, stage 2.
MODPOST
CC /home/ndisk/workshop/testm.mod.o
LD [M] /home/ndisk/workshop/testm.ko
make: Leaving directory `/usr/src/kernels/2.6.18-8.el5-i686'
安装(将编译好的模块放到内核路径下):
[root@zhenfg workshop]# make -C /lib/modules/`uname -r`/build M=$PWD modules_install
make: Entering directory `/usr/src/kernels/2.6.18-8.el5-i686'
INSTALL /home/ndisk/workshop/testm.ko
DEPMOD 2.6.18-8.el5
make: Leaving directory `/usr/src/kernels/2.6.18-8.el5-i686'
如果需要clean工程:
[root@zhenfg workshop]# make -C /lib/modules/`uname -r`/build M=$PWD clean
make: Entering directory `/usr/src/kernels/2.6.18-8.el5-i686'
CLEAN /home/ndisk/workshop/.tmp_versions
make: Leaving directory `/usr/src/kernels/2.6.18-8.el5-i686'
2.2 基于内核源码进行编译
如果你有一套内核源码,并且想基本该内核源码编译模块,则需要使用下面的命令make:
make -C <path_to_kernel_src> M=$PWD
如果你需要交叉编译,那么还需要指定交叉编译工具链:
make -C <path_to_kernel