Linux 内核包含了许多的驱动,这些驱动都是由一个个模块组合而成,那么怎么编写一个简单的内核驱动模块?
模块源码框架
内核的模块需要按照一定的框架进行搭建,通常情况下都需要模块入口和模块出口
#include
#include
static int __init hello_init(void)
{
int i;
for(i = 0; i < 10; i++ ) {
printk(KERN_ALERT "Hey, How are you. digit %d\n", i);
}
return 0;
}
static void __exit hello_exit(void)
{
printk(KERN_ALERT "Bye man, I have been unload.\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_AUTHOR("Jackie Liu ");
MODULE_LICENSE("GPL");
首先需要定义一个 hello_init 的函数,然后通过 module_init 注册到内核中,同理的 hello_exit 也一样。MODULE_AUTHOR 并不是必须的,他仅仅告知使用者本模块的作者是谁,这个虽然对模块使用不产生什么影响,但是使用者尊重作者版权的态度还是要有的。MODULE_LICENSE 也并不是必须的,但是如果不显示声明模块采用 GPL 协议,那么很多的以 EXPORT_SYMBOL_GPL 导出的函数是无法使用的,通常情况下,建议大家都采用 GPL 授权模式,毕竟内核整个框架都是采用的 GPL 授权方案,如果你不懂什么是 GPL,那么请点击这里。
好了,一个简单的模块已经编写完毕,无论多了复杂的驱动,他总是需要一个入口和一个出口,就像一个再复杂的 C 语言程序都需要一个 main 入口一样。
Makefile 的编写
尽管我们按照套路完成了驱动的编写,都这还不够,正确的书写配套的 Makefile 也是成功的关键,内核驱动模块的 Makefile 与其他核外的进程程序不同,他编译出来的对象并不是二进制可执行文件
obj-m := hello.o
DIRS := /lib/modules/`uname -r`/build
all:
make -C $(DIRS) M=$(PWD) modules
clean:
rm -Rf*.o *.ko *.mod.c *.order *.symvers
编译、加载与卸载模块
准备好 Makefile 与 hello.c 源码文件,那么接下来可以演示如何加载与卸载模块
jackieliu@jackieliu-virtual-machine:~/modules$ make
make -C /lib/modules/`uname -r`/build M=/home/jackieliu/modules modules
make[1]: Entering directory '/usr/src/linux-headers-4.11.0-14-generic'
CC [M] /home/jackieliu/modules/hello.o
Building modules, stage 2.
MODPOST 1 modules
CC /home/jackieliu/modules/hello.mod.o
LD [M] /home/jackieliu/modules/hello.ko
make[1]: Leaving directory '/usr/src/linux-headers-4.11.0-14-generic'
编译阶段需要保证没有报错,并且正确的生成 hello.ko。然后使用 root 权限来加载这个模块并打印内核输出信息。
jackieliu@jackieliu-virtual-machine:~/modules$ sudo insmod hello.ko
jackieliu@jackieliu-virtual-machine:~/modules$ dmesg
[ 1760.276063] Hey, How are you. digit 0
[ 1760.276068] Hey, How are you. digit 1
[ 1760.276069] Hey, How are you. digit 2
[ 1760.276070] Hey, How are you. digit 3
[ 1760.276071] Hey, How are you. digit 4
[ 1760.276072] Hey, How are you. digit 5
[ 1760.276073] Hey, How are you. digit 6
[ 1760.276074] Hey, How are you. digit 7
[ 1760.276075] Hey, How are you. digit 8
[ 1760.276075] Hey, How are you. digit 9
dmesg 信息将模块的 hello_init 中需要打印的信息展现出来,表示模块已经正确的加载。然后通过 rmmod 来卸载模块并打印信息。
jackieliu@jackieliu-virtual-machine:~/modules$ sudo rmmod hello
jackieliu@jackieliu-virtual-machine:~/modules$ dmesg
[ 1880.180709] Bye man, I have been unload.
模块正确卸载,并打印信息。至此,一个简单的模块已经介绍完毕,食用愉快。
模块出口并不是一定需要,也可以不写,那么这样的模块在正常模式下是无法卸载的。 ↩︎
这个说法其实页不完全对,可执行文件的组成方式是 ELF 格式,内核模块的文件组成格式也是 ELF 格式。 ↩︎