内核模块开发

    Linux内核模块 便是这样的插件。作为 Linux 内核的扩展手段,可以在运行时动态加载和卸载。那么,一个内核模块是怎么编写的呢?与普通程序编写有什么区别呢?

    程序开发经常以 程序入门,因为这是最简单的。本文也通过一个最简单的内核模块,演示如何一步步编写一个内核模块。

    内核模块与普通程序不同,没有执行流。可以这样理解,内核模块实现一些函数,作为回调函数注册到内核中。在内核加载/卸载时,或者其他应用程序调用系统调用时,注册的回调函数才得到调用。

    有两个最基本的回调函数 initexit ,分别由 module_initmodule_exit 登记,分别在模块加载和卸载的时候执行。下面实现的 hello world 内核模块,将只实现这两个最基本的函数。

    内核模块支持 参数 ,用户借此控制内核模块的行为。参数有默认值,可以在加载时指定,也可以在通过 proc 伪文件系统等手段动态修改。

    此外,内核模块还需要带一些描述信息,包括 许可证作者描述 以及 版本 等等。描述信息由 MODULE 系列宏指定, _modinfo 等命令可以展示这些信息。

    编码

    hello 模块只实现 initexit 函数,分别在加载和卸载时往内核日志输出一条记录。同时,也演示了参数的使用方式——通过参数控制日志输出内容。

    内核模块代码已经写好了,怎样知道写得对不对呢?编译运行一下不就知道了?可问题是如何编译呢?跟普通程序一样吗?用 gcc 来编译?

    没错,内核模块用 gcc 来编译,但不会直接运行 gcc 。我们需要先准备一个 Makefile ,内容如下:

    1. obj-m+=hello.o
    2. make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) modules
    3. make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) modules

    好了之后,我们再命令行下运行 make 即可:

    看到 hello.ko 文件生成,意味着编译大功告成了!该文件就是二进制内核模块目标文件,其他文件的作用不再深入讨论。

    顺便提一句, Makefile 中分成 allclean 两节。其中 all 用来编译, clean 用来清理编译环境:

    1. $ make clean
    2. make -C /lib/modules/3.16.0-4-amd64/build/ M=/home/fasion/hello clean
    3. make[1]: Entering directory '/usr/src/linux-headers-3.16.0-4-amd64'
    4. make[1]: Entering directory `/usr/src/linux-headers-3.16.0-4-amd64'
    5. CLEAN /home/fasion/hello/Module.symvers
    6. $ ls
    7. hello.c Makefile

    运行

    内核模块编译完成后,要怎么使用呢?跟普通程序直接运行不同,内核模块需要加载到内核里面发挥作用。使用 命令加载内核模块:

    内核模块加载后,用 dmesg 命令看到,内核模块初始化时输出的一条内核日志。

    加载内核模块时怎么指定参数的值呢?下面演示移除内核模块,并以 fasionname 参数值重新挂载:

    1. $ sudo rmmod hello
    2. $ sudo insmod hello.ko name=fasion

    操作完成后,用 可以看到以下内核日志:

    第二次挂载时, name 参数的值不是默认值 world ,而是 fasion 了。

    跟内核模块相关还有 insmod 、 、 lsmod 、 、 modprobe 等命令,用法请参考man文档。

    小菜学编程

    参考文献