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

在上一篇博文中,我们已经通过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。

时间: 2024-10-29 03:47:24

第1个linux驱动___安装驱动模块之内核再爱我一次的相关文章

第1个linux驱动___给驱动模块上户口(二)

从这篇博文开始,我们终于可以给我们的驱动模块first_drv一个正式的编制了,现在他已经填好了自己的户口登记信息表,距离拿户口本儿只有一步了! 内核内部有自己的一套安装驱动模块的方法,就像是民政局工作人员知道怎么给一个人办户口一样,对于一个普通公民来说,工作人员做了哪些操作办好了户口本,并不是普通公民需要关心的,我们最关心的是如何把我们填好的户口信息登记表交给工作人员,如何向工作人员表明我们有办户口的请求. 在linux系统中,对于字符设备驱动,我们通过register_chrdev这个函数来

第1个linux驱动___给驱动模块上户口

我们已经成功地通过开发板的最小根文件系统的/mnt目录挂接了虚拟机ubuntu的/work/my_drivers/ko_file目录下的内容,我们只要在最小根文件系统环境中执行:insmod /mnt/first_drv.ko,即可把ubuntu中编译好的first_drv.ko安装到开发板的内核中,卸载执行:rmmod first_drv或rmmod first_drv.ko即可. 下面,先来写一个方便我们挂接到虚拟机ubuntu的/work/my_drivers/ko_file的脚本,在最小

第1个linux驱动___打印&quot;hello world&quot;

为了方便后续的深入,我们在驱动程序中用printk( )函数来打印"hello world",printk( )是内核中自带的函数,专门用于在打印内核信息. 在安装驱动模块到内核中的时,需要进行驱动模块的初始化,初始化具体做什么我们先不提,我们暂时只用printk( )打印"hello world": int first_drv_init(void) { printk("hello world!\n"); return 0; } · 关于prin

第1个linux驱动___应用程序才是大Boss

我们的驱动模块已经可以被自动分配主设备号了,可以说到目前为止,一个驱动模块所具备的一些"基础设施"它都具备了,就像是养兵前日,用兵一时,已经可以让first_drv这个驱动模块出去打仗了. 那么说是出去打仗,总得有个发号施令的首长吧,在linux系统中,这位调兵遣将的首长就是应用程序,应用程序才是大Boss,我们"一直精雕细琢的驱动模块"说白了只是个跑腿的. 这就是为什么我们的专题是第1个linux驱动却要讲应用程序的原因,因为应用程序是让驱动程序能够被应用的,而驱

第1个linux驱动___搭建环境,蓄势待发

如何开始写最简单的linux驱动? 在阅读本文前,你需要对linux的基本知识.ubuntu虚拟机的安装与命令行操作有基本的了解. 环境搭建:我在windows电脑上使用VMware打开安装好的虚拟机ubuntu9.0,ubuntu是运行linux内核的linux发行版. 我习惯secureCRT通过SSH远程登录到虚拟机上操作,当然你也可以直接在虚拟机环境下操作. 在根目录下建立一个目录树: cd / mkdir -p /work/my_drivers/frist_drv/1th 进入底层目录

第1个linux驱动___整个系统是怎样工作的

接着上一篇博文中,我们已经在first_drv.c中写好了空壳驱动程序. 但是编译这个空壳驱动程序之前,我们需要了解整个系统是怎么工作的. 不着急,首先,我们先来了解一点linux驱动的基本概念: linux驱动分为字符设备驱动.块设备驱动.网络设备驱动. 这是按照驱动程序对不同的硬件的操作方式差异而划分的: [ 字符设备驱动 ] 以字节级别的数据量来对硬件设备进行操作,涉及的硬件如:LED.LCD.串口.触摸屏等. [ 块设备驱动 ] 则操作的数据量较大,如读写存储芯片的驱动就是块设备驱动,一

第1个linux驱动___投靠NFS网络文件系统

之前我们一直是在虚拟机ubuntu环境中测试我们的first_drv驱动模块,但是这不是我们的开发方向,在刚开始的学习中我们避免搭建过多的环境,因此选择了只在ubuntu中测试驱动. 我们的模式是: [first_drv.c]-->[使用ubuntu内核源码编译]-->[first_drv.ko(可运行于虚拟机)]--> [ 安装到虚拟机中 ]-->[ hello world! ]-->[ 从虚拟机中卸载 ]-->[ goodbye world... ] 但是我们的最终

第1个linux驱动___编译空壳驱动程序

在上一篇博文中我们已经了解到整个系统中各个层次之间是如何协调如何工作的,应用层发出的命令经由内核下达到驱动层,从而达到操作硬件层的目的. 我们先回顾上上篇博文最后成型的"空壳驱动程序"first_drv.c中的代码: #include <linux/module.h> #include <linux/init.h> static int __init first_drv_init(void) {        printk(KERN_INFO"hell

第1个linux驱动___自动分配主设备号

本篇博客内容不会很多,主要是对这个驱动程序进行一点优化,每次都要cat /proc/devices去查看还有哪些主设备号可用太麻烦了,linux如此受到热捧,显然我们不是每次都得这样做. 其实register_chrdev函数可以帮我们从1~255里面找到一个可用的主设备号,然后将这个主设备号返回给我们,当然这次我们同样要给register_chrdev函数传一个数字进去,只不过这次不是传100了,而是传0进去,当我们传0的时候,register_chrdev函数就会去查找还有哪个主设备号还可以