欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Linux驱动 | 如何加载驱动模块?

程序员文章站 2022-06-08 22:04:08
...

0x00  前期准备

主机/work/mydrv/目录下,已经写好了驱动层的C文件:mydrv.c,应用层的C文件:mytest.c,以及用来把mydrv.c生成驱动模块(.ko文件)的Makefile
本实验的目的是:把已经写好的LED的驱动程序mydrv.c编译后加载到开发板上;mytest.c编译及运行,实现通过对设备文件的操作来控制开发板上LED的亮灭。

0x01  编译出驱动模块

先进入/work/mydrv/目录查看一下
$ cd /work/mydrv
$ ls
Makefile  mydrv.c mytest.c

输入make,开始编译驱动模块

$ make

等待编译完成,再查看一下
$ ls
Makefile  Module.symvers mydrv.c mydrv.ko mydrv.mod.c mydrv.mod.o mydrv.o mytest.c

0x02  编译应用层程序

$ arm-linux-gcc mytest.c -o mytest

查看一下目录下是否生成mytest:

$ ls
Makefile  Module.symvers mydrv.c mydrv.ko mydrv.mod.c mydrv.mod.o mydrv.o mytest mytest.c

可见经过以上两步已经编译出来了驱动模块mydrv.ko和用来测试的应用层程序mytest

0x03  把驱动模块和测试程序转移到开发板上

这里,我们使用/work/nfs_root/作为网络根文件系统来,所以我们只需要把程序放到这个目录,就相当于放到开发板上面去了。

$ cp ./mydrv.ko ./mytest /work/nfs_root

$ cd /work/nfs_root

$ ls mydrv.ko mytest -l

Linux驱动 | 如何加载驱动模块?
现在,这两个文件已经放上去了,我们就可以通过串口控制开发板来使用它们了!

0x04  加载驱动

先加载驱动模块,然后查看是否已加载,命令如下:

# insmod mydrv.ko

# lsmod

Linux驱动 | 如何加载驱动模块?

通常我们还会经常使用到这样两个命令:cat /proc/devices”,以及“ls /dev/$(DRVICE) -l”,它们分别用来查看向内核注册的设备、设备下的设备文件(应用程序操作的就是 这些文件)。比如说,在这个例子中,我在mydrv.c里面就创建了一个名为“mydrv”的设备,同时创建了一个名为“Hamburger”的设备文件,所以这里的$(DRVICE)应该换为Hamburger
我们来试一下:

# cat /proc/devices

Linux驱动 | 如何加载驱动模块?

我们可以看到mydrv就在其中了,它前面的“252”这个数字表示的就是它的“主设备号”。

# ls /dev/Hamburger -l

Linux驱动 | 如何加载驱动模块?

以上就可以得知驱动模块已加载好了。

0x05  卸载驱动

卸载驱动用的是rmmod这个命令,这里先卸载后查看:

# rmmod mydrv

# lsmod

Linux驱动 | 如何加载驱动模块?
可见驱动已经卸载了。再来查看一下/proc/devices和设备文件:

# cat /proc/devices

# ls /dev/Hamburger -l

Linux驱动 | 如何加载驱动模块?


Linux驱动 | 如何加载驱动模块?

以上就可以得知驱动已经卸载成功了。 

0x06  测试驱动

现在先把驱动模块加载上去:

# insmod mydrv.ko

接着运行测试程序mytest

# ./mytest on  

可以看见开发板上面的LED点亮了

# ./mytest off

执行之后LED灭了

以上说明我们的驱动模块符合我们的预期,测试成功!

0x07  mknod

说一下mknod这个命令,它其实就是在/dev/目录下创建一个设备文件,同时指定设备类型,主设备号,次设备号,比如:

# mknod /dev/Coke c 252 1

这时我们就创建了一个名为Coke的设备文件,并指定为字符类设备,主设备号为252,次设备号为1

/dev/目录查看一下:

# ls /dev/Coke /dev/Hamburger -l

Linux驱动 | 如何加载驱动模块?

如果要删除它,就像操作普通的文件一样:
# rm -f /dev/Coke
再说一下次设备号,它其实是可以由我们任意分配的,比如说:把Coke的次设备号也设为0

# mknod /dev/Coke c 252 0

# ls /dev/Coke /dev/Hamburger -l

Linux驱动 | 如何加载驱动模块?
可见两者并不冲突。

0x08  两个问题

/dev/Hamburger这个设备文件,我们并没有使用mknod命令去创建它,那它是怎么来的呢?

是这样的,在mydrv.c的代码,有下面两行,它调用了内核的class_createclass_device_create函数,创建了一个名为“mydrv”的class,并且在这个class之下创建了一个名为“Hamburger”的class_dev

mydrv_class = class_create(THIS_MODULE, "mydrv");
mydrv_class_dev = class_device_create(mydrv_class, NULL, MKDEV(major, 0), NULL, "Hamburger");

那么当mydrv_classmydrv_class_dev这两个数据结构传入到内核后,内核busyboxmdev(把它看作内核的一个工具就可以)就会自动帮我们去做“mknod /dev/Hamburger c 252 0”了!

主设备号为252,它又是怎么来的呢?
它是系统给我们分配的,同样,在驱动代码里调用了内核的register_chrdev这个函数向内核注册驱动,函数的返回值就是主设备号

major = register_chrdev(0, "mydrv", &mydrv_fops);

0x09  总结

这个例子总结的框图如下:

Linux驱动 | 如何加载驱动模块?

从中我们也可以看出嵌入式开发的特点:代码都是在Linux主机上编译的,而加载、卸载和执行都被放到了嵌入式系统(开发板)上。