第1个linux驱动___安装驱动模块之内核再爱我一次

原创2016-11-16 09:51:10360
摘要:通过Makefile编译得到first_drv.ko文件,这是一个可以被安装到ubuntu中的驱动模块,要怎样做呢?在/work/my_drivers/first_drv/1th/目录下执行:insmod first_drv.ko如果你是在普通用户状态下执行的这条命令,可以看到系统提醒我们:insmod: error inserting 'first_drv.ko': -1 Ope

通过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 okir@monad.swb.de).

[   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。


发布手记

热门词条