本文共 7213 字,大约阅读时间需要 24 分钟。
原文地址: 作者:
设备驱动程序就是这个进入Linux内核世界的大门。设备驱动程序在Linux内核中扮演着特殊的角色。它是一个独立的“黑盒子”,使某个特定硬件响应一个定义好的内部编程接口,这些接口完全隐藏了设备的工作细节。用户的操作通过一组标准化的调用执行,而这些调用独立于特定的驱动程序。将这些调用映射到作用于实际硬件的设备特有操作上,则是设备驱动程序的任务。
块设备:一个块设备驱动程序主要通过传输固定大小的数据来访问设备。块设备和字符设备的区别仅仅在于内核内部管理数据的方式,也就是内核及驱动程序之间的软件接口,而这些不同对用户程序是透明的。在内核中,和字符驱动程序相比,块驱动程序具有完全不同的接口。
网络接口:任何网络事务都经过一个网络接口形成,即一个能够和其他主机交换数据的设备。它可以是个硬件设备,但也可能是个纯软件设备。访问网络接口的方法仍然是给它们分配一个唯一的名字(比如eth0),但这个名字在文件系统中不存在对应的节点。内核和网络设备驱动程序间的通信,完全不同于内核和字符以及块驱动程序之间的通信,内核调用一套和数据包传输相关的函数而不是read、write等。
(1)驱动模块运行在内核空间,运行时不能依赖于任何标准C库等应用层的库、模块,所以在写驱动时所调用的函数只能是作为内核一部分的函数,即使用“EXPORT_SYMBOL”导出的函数。
àinsmod使用公共内核符号表来解析模块中未定义的符号。公共内核符号表中包含了所有的全局内核项(即函数和变量的地址),这是实现模块化驱动程序所必须的。 àLinux使用模块层叠技术,我们可以将模块划分为多个层,通过简化每个层可缩短开发周期。如果一个模块需要向其他模块导出符号,则使用下面的宏:
EXPORT_SYMBOL(name); EXPORT_SYMBOL_GPL(name); |
符号必须在模块文件的全局变量部分导出,因为这两个宏将被扩展为一个特殊变量的声明,而该变量必须是全局的。
(2)驱动模块和应用程序的一个重要不同是:应用程序退出时可不管资源释放或者其他的清除工作,但模块的退出函数必须仔细撤销初始化函数所作的一切,否则,在系统重新引导之前某些东西就会残留在系统中。
(3)处理器的多种工作模式(级别)其实就是为了操作系统的用户空间和内核空间设计的。在Unix类的操作系统中只用到了两个级别:最高和最低级别。
(4)要十分注意驱动程序的并发处理。
(5)内核API中具有双下划线(_ _)的函数,通常是接口的底层组件,应慎用。
(6)内核代码不能实现浮点数运算。参考资料:
利用Linux设备驱动程序的第一个例程:Hello World模块了解内核驱动模块的结构。
#include <linux/init.h> #include <linux/module.h> static int hello_init(void) { printk(KERN_ALERT "Hello, Tekkaman Ninja !\n"); return 0; } static void hello_exit(void) { printk(KERN_ALERT "Goodbye, Tekkaman Ninja !\n Love Linux !Love ARM ! Love KeKe !\n"); } module_init(hello_init); module_exit(hello_exit);
MODULE_LICENSE("Dual BSD/GPL"); |
1. 所有模块代码中都包含一下两个头文件:
#include <linux/init.h> #include <linux/module.h> |
MODULE_LICENSE("Dual BSD/GPL"); |
此外还有可选的其他描述性定义:
MODULE_AUTHOR(""); MODULE_DESCRIPTION(""); MODULE_VERSION(""); MODULE_ALIAS(""); MODULE_DEVICE_TABLE(""); |
上述MODULE_声明习惯上放在文件最后。
3. 初始化和关闭初始化的实际定义通常如下:
static int _ _init initialization_function(void) { /*初始化代码*/ } module_init(initialization_function) |
清除函数的实际定义通常如下:
static int _ _exit cleanup_function(void) { /*清除代码*/ } module_exit(cleanup_function) |
4. 一个简单的Makefile文件:
KERNELDIR = /home/tekkaman/working/SBC2440/linux-2.6.22.2 PWD := $(shell pwd) INSTALLDIR = /home/tekkaman/working/rootfs/lib/modules CROSS_COMPILE = arm-9tdmi-linux-gnu- CC = $(CROSS_COMPILE)gcc obj-m := hello.o .PHONY: modules modules_install clean modules: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules modules_install: cp hello.ko $(INSTALLDIR) clean: rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions |
obj-m := hello.o
代表了我们要构造的模块名为hell.ko,make 会在该目录下自动找到hell.c文件进行编译。如果 hello.o是由其他的源文件生成(比如file1.c和file2.c)的,则在下面加上(注意红色字体的对应关系):
hello-objs := file1.o file2.o ...... |
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
-C $(KERNELDIR) 指定了内核源代码的位置,其中保存有内核的顶层makefile文件。
M=$(PWD) 指定了模块源代码的位置
modules目标指向obj-m变量中设定的模块。
5. 编译模块make modules 、 make modules_install 。
[root@Tekkaman-Ninja Helloworld]# make modules make -C /home/tekkaman/working/SBC2440/linux-2.6.22.2 M=/home/tekkaman/working/Linuxdriver/Helloworld modules make[1]: Entering directory `/home/tekkaman/working/SBC2440/linux-2.6.22.2' CC [M] /home/tekkaman/working/Linuxdriver/Helloworld/hello.o Building modules, stage 2. MODPOST 1 modules CC /home/tekkaman/working/Linuxdriver/Helloworld/hello.mod.o LD [M] /home/tekkaman/working/Linuxdriver/Helloworld/hello.ko make[1]: Leaving directory `/home/tekkaman/working/SBC2440/linux-2.6.22.2' [root@Tekkaman-Ninja Helloworld]# make modules_install cp hello.ko /home/tekkaman/working/rootfs/lib/modules [root@Tekkaman-Ninja Helloworld]# |
[Tekkaman2440@SBC2440V4]#cd /lib/modules/ [Tekkaman2440@SBC2440V4]#ls cs89x0.ko hello.ko p80211.ko prism2_usb.ko [Tekkaman2440@SBC2440V4]#insmod hello.ko Hello, Tekkaman Ninja ! [Tekkaman2440@SBC2440V4]#lsmod Module Size Used by Not tainted hello 1376 0 [Tekkaman2440@SBC2440V4]#rmmod hello Goodbye, Tekkaman Ninja ! Love Linux !Love ARM ! Love KeKe ! [Tekkaman2440@SBC2440V4]#lsmod Module Size Used by Not tainted [Tekkaman2440@SBC2440V4]# |
struct something *item1; struct somethingelse *item2; int stuff_ok; void my_cleanup(void) { if (item1)release_thing(item1); if (item2) release_thing2(item2); if (stuff_ok) unregister_stuff(); return; } int __init my_init(void) { int err = -ENOMEM; item1 = allocate_thing(arguments); item2 = allocate_thing2(arguments2); if (!item2 || !item2) goto fail; err = register_stuff(item1, item2); if (!err) stuff_ok = 1; else goto fail; return 0; /* success */fail: my_cleanup( ); return err; } |
转载地址:http://ntomi.baihongyu.com/