以前断断续续也看过一些linux驱动程序的内容,只知道这个很高深东西。也想过学习一下,但是来来回回,始终实在HelloWorld阶段,话说这个HelloWorld模块已经被我写了n遍了。汗
终于我决心要系统的走一边这个流程,于是周末去图书馆借了一本讲嵌入式驱动的书,《深入浅出嵌入式底层软件开发》主要从这本书的第8章开始看起。
在学习驱动之前,首先要明确:设备驱动程序的作用是在于提供机制,而不是提供策略,编写访问硬件的内核代码时,不要给用户强加任何策略。这个需要特别注意。
通常我们需要为每个驱动创建一个不同的模块,而不是在一个模块中实现多个设备的驱动。这里使用的思想是最小模块化,而不是通用化。最小模块化的好处在于可以实现良好的伸缩性和扩展性,并且在编写代码的时候,思路也会很清晰不需要考虑很多。
Linux系统将设备驱动分成三种:
- 字符设备
- 块设备
- 网络设备驱动和网络接口
字符设备:能够像字节流(如文件)一样访问设备
块设备:按照块为单位来访问数据,一块为512k,和字符设备的区别就是内核内部管理方式不同,应用程序一般通过文件系统间接地对快设备进行操作,块设备和文件系统关系紧密
网络设备驱动和网络接口:和字符设备和块设备有很大的不同
还是拿我写了很多遍的HelloWorld来举例,用来看看一个最简单的模块是如何生产、加载、卸载的。
首先是写代码,如下
hello.c
#include <linux/module.h> #include <linux/init.h> MODULE_LICENSE("GPL"); void __init hello_init(void) //加载 { printk("<0>Hello World! init\n"); } void __exit hello_exit(void) //卸载 { printk("<0>Hello World! exit\n"); } module_init(hello_init); //此处可以省略,因为在函数前已经有__init修饰 module_exit(hello_exit); //此处可以省略,因为在函数前已经有__exit修饰
Makefile
obj-m:=hello.o PWD:=$(shell pwd) KVER:=$(shell uname -r) KDIR:=/lib/modules/$(KVER)/build all: $(MAKE) -C $(KDIR) M=$(PWD) clean: rm -rf *.o *.mod.c *.ko *.symvers *.order *.markers *~
把上面的两个文件放在同一个目录下面,然后运行命令行,
打入sudo make等待编译完成。
完成后,原来目录中多了好多文件,其中一个叫hello.ko,这个就是我们需要加载的文件。
在命令行中打入sudo insmod hello.ko,模块就加载了。
可以通过dmesg命令来查看加载是输出的信息,通过lsmod加grep可以查询对应模块是否已经加载,modinfo可以用来查看模块信息。
sudo rmmod hello.ko可以卸载模块。