Linux驱动程序开发
用户态和内核态
Linux操作系统分为用户态和内核态。用户态处理上层的软件工作。内核态用来管理用户态的程序,完成用户态请求的工作。驱动程序与底层的硬件交互,所以工作在内核态。
Linux操作系统分为两个状态的原因主要是,为应用程序提供一个统一的计算机硬件抽象。工作在用户态的应用程序完全可以不考虑底层的硬件操作,这些操作由内核态程序来完成。这些内核态程序大部分是设备驱动程序。一个好的操作系统的驱动程序对用户态应用程序应该是透明的,也就是说,应用程序可以在不了解硬件工作原理的情况下,很好地操作硬件设备,同时不会使硬件设备进入非法状态。Linux操作系统很好的做到了这一点。
一个值得注意的问题是:工作在用户态的应用程序不能因为一些错误而破坏内核态的程序。现代处理器已经充分考虑了这个问题。处理器提供了一些指令,分为特权指令和普通指令。特权指令只有在内核态下才能使用;普通指令既可以在内核态使用,也可以在用户态使用。通过这种限制,用户态程序就不能执行只有在用户态才能执行的程序了,从而起到保护的作用。
另一个值得注意的问题是:用户态和内核态是可以互相转换的。每当应用程序执行系统调用或者被硬件中断挂起时,Linux操作系统都会从用户态切换到内核态。当系统调用完成或者中断处理完成后,操作系统会从内核态返回用户态,继续执行应用程序。
模块机制
模块是可以在运行时加入内核的代码,这是Linux一个很好的特性。这个特性使内核可以很容易地扩大或者缩小,一方面扩大内核可以增加内核的功能,另一方面缩小内核可以减小内核的大小。
Linux内核支持很多种模块,驱动程序就是其中最重要的一种,甚至文件系统也可以写成一个模块,然后加入内核中。每一个模块由编译好的目标代码组成,可以使用insmod命令将模块加入正在运行的内核,也可以使用rmmod命令将一个未使用的模块从内核中删除。试图删除一个正在使用的模块,将是不允许的。
模块在内核启动时装载称为静态装载,在内核已经运行时装载称为动态装载。模块可以扩充内核所期望的任何功能,但通常用于实现设备驱动程序。一个模块的最基本框架代码如下:
#include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> int __init xxx_init(void) { /*模块加载时的初始化工作*/ return 0; } void __exit xxx_exit(void) { /*模块卸载时的销毁工作*/ } module_init(xxx_init); /*指定模块的初始化函数的宏*/ module_exit(xxx_exit); /*指定模块的卸载函数的宏*/