在上一期中,我们介绍了Linux内核编译方法,这一期我们用一个例子来介绍如何向Linux内核中增加一个模块。

一、LKM内核模块

LKM是Loadable Kernel Module的缩写,意思是可加载内核模块。它有点儿像动态链接库,可在运行时加载,成为内核代码和数据的一部分,访问全部内核地址空间,也可运行时动态卸载(需要解决依赖关系,并释放内存空间),所谓的更新就是加载→卸载→加载的过程。LKM是大部分设备驱动、文件系统的存在形式。

为了编写内核模块,我们需要准备好编写的环境。首先,要指定内核源码或源码的头文件(即本模块是为哪个内核版本编写的),然后可以用VIM等编辑器编写,最后可以用gcc编译。

下图展示了一个简单的内核模块的示例代码:

在示例代码中我们可以看到两个重要的宏:moduleinit和moduleexit。这两个宏包含在init.h头文件中,规定了模块的入口和出口函数。而MODULELICENSE、MODULEAUTHOR和MODULE_DESCRIPTION三个函数则被包含在module.h头文件中[4]。

为了将这个模块的源码编译成可动态加载的内核模块,我们还需要修改Makefile:

注意,运行模块代码有两种方式,第一种方法是静态编译连接进内核,在系统启动过程中进行初始化;第二种方法是编译成可动态加载的module,通过insmod动态加载重定位到内核。这两种方式可以在Makefile中通过obj-y或obj-m选项进行选择[1]。使用obj  –m选项编译之后会生成.ko文件,可通过insmod将模块动态加载到内核中。

采用动态加载的方式有两个优点,一是可根据系统需要运行动态加载模块,以扩充内核功能,不需要时将其卸载,以释放内存空间;二是当需要修改内核功能时,只需编译相应模块,而不必重新编译整个内核[1]。

有五个与动态加载内核模块有关的命令[2]:

1.insmod:向Linux内核中插入一个模块;

2.rmmod:卸载内核中的模块;

3.lsmod:显示内核中的模块 ;

4.modprobe:可载入指定的个别模块,或是载入一组相依的模块。modprobe会根据depmod所产生的相依关系,决定要载入哪些模块。若在载入过程中发生错误,在modprobe会卸载整组的模块[3]。

5.modinfo:显示kernel模块的对象文件,以显示该模块的相关信息。

有时侯一个模块可能要调用其他模块中的函数,如果内核模块要引用内核代码中的符号则要通过内核符号表。内核符号表记录了内核中所有的符号(函数、全局变量等)的地址以及名字,在内核代码中通过printk("%pS\n",addr)可以打印符号名[5]。使用EXPORTSYMBOL可以将一个函数或全局变量以符号的方式导出给其他模块使用在使用时应先在被调用函数之后用EXPORTSYMBOL(函数名)将函数导出,然后在调用该函数的模块中用extern关键字引用该函数。在加载模块时应注意顺序,首先加载定义该函数的模块,然后加载调用该函数的模块[6]。内核启动后生成/proc/kallsyms,它包含了内核中的函数符号和全局变量。

 

二、结语

本期通过一个实例介绍如何编写和编译一个新的内核模块,下一期我们将对Linux各版本进行对比分析。

参考文献

[1]https://blog.csdn.net/yanxuan321/article/details/86606329

[2]https://blog.csdn.net/CPU1994GHz/article/details/79309221?utm_source=blogxgwz3

[3]https://baike.baidu.com/item/modprobe/7939608?fr=aladdin

[4]https://www.xuebuyuan.com/3181553.html

[5]https://www.cnblogs.com/sky-heaven/p/6297679.html

[6]https://www.cnblogs.com/Caden-liu8888/p/7725293.html

 

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐