所谓驱动程序,本质上讲是硬件接口,因为操作系统不可能实现每种硬件的接口,所以只对厂商提供接口,只要厂商实现这些接口,就可被操作系统调用,Linux系统驱动程序分为字符设备驱动和块设备驱动,所谓字符设备驱动就是例如键盘驱动,只能顺次读取数据,块设备驱动入硬盘等,可以随机分块读取。而有些程序虽然符合驱动程序规范,但却不真正驱动硬件,而是对操作系统功能的扩充,也称作内核模块。所以驱动程序和内核模块本质上讲属于同一种类别。
操作系统对字符设备驱动提供 file_operations 结构,该结构成员大部分都是回调函数(以下代码摘自Linux 2.6.34内核源码):
作为驱动程序,只需要实现部分回调函数,注册该结构体,用户态进程便可通过系统调用open,read等调用相应回调函数指针。
首先,系统提供若干宏声明驱动程序的属性,如,入口,作者,描述信息等等。
初始化程序原型为 static int __init initialization(void);若初始化成功则返回0,否则返回错误码。
清理函数原型为static void __exit cleanup(void);其中__init __exit是指定代码段属性的宏,当然也可不指定此属性。
另外宏MODULE_AUTHOR指明作者等等
实现该函数后便可通过宏指明入口点:
最简单的驱动程序就是仅仅实现这两个函数,文件simple.c如下:
驱动的编译需要写Makefile文件,内容如下
obj-m := simple.o
编译时需指定Linux内核源码所处位置:
make -C /usr/src/linux M=$PWD modules
其中/usr/src/linux为当前内核源码目录,$PWD为驱动程序所处目录,PWD为当前目录。
执行成功后,会生成simple.ko文件,此文件即为驱动程序。
加载驱动程序可执行 insmod simple.ko 卸载驱动执行 rmmod simple
命令 lsmod 可以查看目前系统加载的驱动程序,modinfo simple.ko 查看程序的基本信息,输出即为程序声明信息:
在程序中调用了两次printk,为内核输出函数,这里的输出不会显示到控制台,只会输出到内核,可以通过读取/proc/kmsg文件查看信息,或者调用dmesg命令查看,此为内核跟踪错误的重要手段。KERN_INFO宏只是一个数字字符串,含义为日志级别,可以通过echo num > /proc/sys/kernel/printk 来控制输出信息的级别。
当然,只有此两个函数只能正常加载卸载驱动程序,并没有任何意义,下面通过注册回调函数来实现字符设备的功能,只举一个简单的例子,实现open,read,close函数。
根据file_operations结构成员的原型,这里我们需要实现如下回调:
open和close函数为了简单起见不做任何处理,只是简单的输出kernel信息: