在上一篇博文中,我们已经通过Makefile编译得到first_drv.ko文件,这是一个可以被安装到ubuntu中的驱动模块,要怎样做呢?
在/work/my_drivers/first_drv/1th/目录下执行:insmod first_drv.ko
如果你是在普通用户状态下执行的这条命令,可以看到系统提醒我们:insmod: error inserting ‘first_drv.ko‘: -1 Operation not permitted
这是因为安装驱动模块需要超级权限,你可以在普通用户状态下执行:sudo insmod first_drv.ko,或直接切换到root用户,执行insmod first_drv.ko
回车后发现什么事都没有发生,其实在我们执行insmod first_drv.ko的时候,就相当于调用了first_drv.c中的module_init(first_drv_init),我们在以前的博文中说过module_init( )定义了一个结构体,当我们写成module_init(first_drv_init)时,那个结构体中就会有一个函数指针指向了first_drv_init函数,从而跳转执行first_drv_init函数中的内容:
static int __init first_drv_init(void) { printk(KERN_INFO"hello world!\n"); return 0; }
容易得知,该函数被调用时会打印出"hello world!",但是如果我们分析的没错的话,为何刚刚又没有看到命令行中输出"hello world!"呢。
可能有人会想是不是打印级别的原因,于是输入cat /proc/sys/kernel/printk,
打印:4 4 1 7
第一个果然是4,当前打印级别"KERN_INFO"为6,因此改为7,即可让"KERN_INFO"满足打印出"hello world!"的打印级别,但事实上,即便改成7后,我们执行insmod first_drv.ko后依旧看不出命令行中有任何动静。
事实上是这样的,ubuntu中严格限制printk输出的数据显示在命令行中,这内部有一套机制使得我们不论如何设置打印级别,也看不到"hello world!"。
但好在我们可以通过执行dmesg命令来查看ubuntu隐藏起来的内核打印信息:
[ 0.000000] Initializing cgroup subsys cpuset
[ 0.000000] Initializing cgroup subsys cpu
···
[ 0.000000] TSC: Frequency read from the hypervisor
[ 0.000000] Detected 2501.000 MHz processor.
[ 0.001144] Console: colour VGA+ 80x25
[ 0.001146] console [tty0] enabled
···
[ 2.665131] EXT4-fs: file extents enabled
[ 2.666123] EXT4-fs: mballoc enabled
[ 2.666135] EXT4-fs (sdb1): mounted filesystem with ordered data mode
[ 2.737083] Installing knfsd (copyright (C) 1996 [email protected]).
[ 2.811797] eth4: link up
[ 5.129212] svc: failed to register lockdv1 RPC service (errno 97).
[ 5.129735] NFSD: Using /var/lib/nfs/v4recovery as the NFSv4 state recovery directory
[ 5.129842] NFSD: starting 90-second grace period
[ 14.218140] eth4: no IPv6 routers present
[ 979.436592] first_drv: module license ‘unspecified‘ taints kernel.
[ 979.436620] Disabling lock debugging due to kernel taint
[ 979.437252] hello world!
最后三行打印信息是和我们安装first_drv.ko驱动模块相关的,我用红色标注了,而最后一行打印信息正是我们梦寐以求的"hello world!",这正是我们安装驱动模块的过程中成功调用了first_drv_init函数的铁证。
但是我们驱动模块很简单,除了我们手写的"hello world!"和"goodbye world..."外再也没有可能打印别的了,那么倒数第3行和倒数第2行是怎么回事?
这是在安装驱动模块的过程中,系统找不到驱动程序的许可证信息,还记得我们之前说first_drv.c这个驱动还有一个不足之处吗,这就是它的不足之处,这就是好像你经营一家餐馆,但是没有卫生许可证,就算饭菜的味道再美味,顾客用餐还是会有点忧心忡忡。
其实说白了这种许可证只是一个形式,哪家的许可证都差不多,说到底许可证和食物是否卫生没有必然的联系,关键还是得看做饭菜的人有没有注意卫生。
我们的模块许可证也是一样,和我们的程序没有半点关系,程序写得再糟糕,只要加个模块许可证,内核会一声不吭地就接纳你,如果你代码写得再规范,唯独丢了模块许可证,那么内核还是会嘟嚷几句:"first_drv: module license ‘unspecified‘ taints kernel."、"Disabling lock debugging due to kernel taint",但驱动模块还是能够工作的,但为了更加符合内核的要求,我们最好还是养成良好的习惯,加上模块许可证,免得出现意想不到的麻烦。
我们只需要在first_drv.c最后再加一句:MODULE_LICENSE("GPL");即可。
符合内核规范的first_drv.c如下所示:
#include <linux/module.h> #include <linux/init.h> static int __init first_drv_init(void) { printk(KERN_INFO"hello world!\n"); return 0; } static int __exit first_drv_exit(void) { printk(KERN_INFO"goodbye world...\n"); return 0; } module_init(first_drv_init); module_exit(first_drv_exit); MODULE_LICENSE("GPL");
执行make重新编译first_drv.c,新生成的first_drv.ko即可覆盖旧的first_drv.ko,因此不必每次都执行make clean删除旧文件。
但是旧的first_drv.ko还安装在内核上,在安装新的first_drv.ko之前,我们如何卸载旧的first_drv.ko呢。
执行lsmod可以查看ubuntu中已经安装的驱动模块,在打印出的一大页信息的最上面可以看到:
Module Size Used by
first_drv 1020 0
binfmt_misc 8356 1
nfsd 241104 9
exportfs 4412 1 nfsd
nfs 271880 0
这说明first_drv驱动模块还在内核中,我们来使用rmmod命令来卸载它:
执行sudo rmmod first_drv或者sudo rmmod first_drv.ko(在root用户状态下不需要加sudo)
回车后一样没有任何事情发生,执行dmesg可以看到最后一行多了:
[ 2333.481131] goodbye world...
这正是first_drv_exit函数中打印的"goodbye world...",与讲解insmod first_drv.ko时一样的原理,执行rmmod first_drv.ko时,就相当于去调用first_drv.c中的module_exit(first_drv_exit),然后调用first_drv_exit函数,打印出"goodbye world..."信息。
其实ubuntu每次只会对没有许可证的驱动模块发出一次警告打印信息,对于同一个没有许可证的驱动,即便卸载再重装,ubuntu就再也不会打印警告信息,有一种对该驱动程序“彻底失望”的意味,为了让系统重新认识我们新的驱动程序,并确定在添加了许可证后内核不会再打印警告信息,我们将ubuntu重启,重启后,在该目录下执行sudo insmod first_drv.ko后,再执行dmesg打印驱动安装信息:
...
[ 5.714574] svc: failed to register lockdv1 RPC service (errno 97).
[ 5.715109] NFSD: Using /var/lib/nfs/v4recovery as the NFSv4 state recovery directory
[ 5.715371] NFSD: starting 90-second grace period
[ 14.690813] eth4: no IPv6 routers present
[ 89.383529] hello world!
只打印了"hello world!",可以证明添加了驱动模块的"GPL"许可证后,ubuntu内核已经完全认可了我们的驱动模块first_drv。