在学习C/C++语言,入门的程序都是输出hello world,那么学习内核模块编程,也从输出Hello World开始。
首先,从最简单模块开始。
1)编写程序hello-1.c
#include <linux/module.h>
#include <linux/kernel.h>
int init_module(void)
{
printk(KERN_INFO "Hello World 1.\n");
return 0;
}
void cleanup_module(void)
{
printk(KERN_INFO "Goodbye world 1.\n");
}
内核模块必须至少有两个函数:一个“开始”(初始化)函数——称之为init_module(),当模块加载(insmod)到内核时,将调用该函数;一个“结束”(清理)函数——称之为cleanup_module(),当卸载模块(rmmod)时将调用该函数。
然而,从内核2.3.13开始,已经对这种情况进行了改进,可以使用任何函数名作为模块的初始化函数和清理函数,接下来再讲解。
一般地,init_module()要么在kernel中注册一个处理程序,要么用它自己的代码取代内核中的代码。
cleanup_module()函数所做的工作与init_module()的相反。这样以使模块可以安全的卸载。
最后,每个内核模块需要包含linux/module.h,我们也需要包含linux/kernel.h,该宏仅仅用于printk()的log等级的宏扩展,像KERN_ALERT,KERN_INFO等。
2)编写Makefile
obj-m += hello-1.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
内核模块的编译方式和常规的用户应用程序有一点不同,前者,内核版本需要我们更多的关注存在在Makefiles中的设置,尽管分层的组织使很多的冗余设置累积在底层的Makefile中,以使Makefile变得很大,而且很难维护,幸运的是,有一种成为kbuild的东西解决了这问题,它可以让外部加载模块的构建过程可以完整的整合到标准内核构建机制中,学习更多关于如何编译不是官方内核的模块,可以参考文件/linux/Documentation/kbuild/modules.txt。
对于上面的Makefile中的内容,只有第一行是必须的,“all”和“clean”目标的加入纯粹是为了方便。
更多的关于内核模块的Makefiles细节见linux/Documentation/kbuild/makefiles.txt。在开始编写Makefile之前阅读一下该文件以及相关的文件,将会节省大量的工作。
3)编译&加载
首先,编译,如下图:
注意到内核2.6之后引入了一种新的文件命名规定:内核模块现在有了一个.ko扩展名(取代了老版的.o扩展名),容易与传统的目标文件区分开来,原因是包含附加的.modinfo部分(附加的关于模块的信息)。
使用modinfo hello-1.ko来查看模块的信息,如下图:
加载最新编译的模块到内核中,使用如下的命令:
insmod ./hello-1.ko
所有加载到内核的模块在/proc/modules文件中列出,如下图:
4)模块卸载
使用命令:
rmmod hello-1
查看/var/log/syslog,查看系统日志文件。如下: