
目录
1. Linux设备驱动
1.1 驱动程序
设备驱动程序(DeviceDriver),简称驱动程序(Driver)。它是一个允许计算机软件 (ComputerSoftware)与硬件(Hardware)交互的程序。这种程序建立了一个硬件与硬件, 或硬件与软件沟通的界面。CPU经由主板上的总线(Bus)或其他沟通子系统(Subsystem) 与硬件形成连接,这样的连接使得硬件设备(Device)之间的数据交换成为可能。
依据不同的计算机架构与操作系统差异平台,驱动程序可以是8位(8b)、16位(16b)、 32位(32b),64位(64b),这是为了调和操作系统与驱动程序之间的依存 关系。例如在Windows3.11的16位操作系统时代,大部分的驱动程序都是16位;到了 32位的WindowsXP,则大部分是使用32位驱动程序;至于64位的Linux或是Windows Vista平台上,就必须使用64位驱动程序。
1.2 驱动设备
计算机系统的主要硬件由CPU、存储器和外部设备组成。驱动程序的对象一般是存储器和外部设备。随着芯片制造工艺的提高,为了节约成本,通常将很多原属于外部设备的控制器嵌入到CPU内部。所以现在的驱动程序应该支持CPU中的嵌入控制器。Linux将这些设备分为3大类,分别是字符设备、块设备、网络设备。
1.2.1 字符设备
字符设备是指那些能一个字节一个字节读取数据的设备,如LED灯、键盘、鼠标等。字符设备一般需要在驱动层实现open()、 cloge0 、read()、write()、ioctl()等函数。这些函数最终将被文件系统中的相关函数调用。内核为字符设备对应一个文件,如字符设备文件/dev/cosole 。对字符设备的操作可以通过字符设备文件 |dev/cosole 来进行。这些字符设备文件与普通文件没有太大的差别,差别之处是字符设备一般不支持寻址,但特殊情况下,有很多字符设备也是支持寻址的。
1.2.2 块设备
块设备与字符设备类似,一般是像磁盘一样的设备。在块设备中还可以容纳文件系统,并存贮大量的信息。在Linux系统中,进行块设备读写时,每次只能传输一个或者多个块。Linux可以让应用程序像访问字符设备一样访问块设备,一次只读取一个字节。所以块设备从本质上更像一个字符设备的扩展,块设备能完成更多的工作,例如传输一块数据。
1.2.3 网络设备
计算机连接到互联网上需要一个网络设备,网络设备主要负责主机之间的数据交换。与字符设备和块设备完全不同,网络设备主要是面向数据包的接收和发送而设计的。网络设备在Linux操作系统中是一种非常特殊的设备,其没有实现类似块设备和字符设备的read()、write()、ioctl()等函数。网络设备实现了一种套接字接口,任何网络数据传输都可以通过套接字来完成。
| 类型 | 特点 | 访问方式 | 典型例子 |
|---|---|---|---|
| 字符设备 | 以字节流为单位进行顺序访问。 | 通过设备文件(如 /dev/ttyS0),使用 open, read, write, close 等系统调用。 | 键盘、鼠标、串口、打印机、大部分简单设备。 |
| 块设备 | 以数据块为单位进行随机访问。 | 也通过设备文件(如 /dev/sda1),但内核有专门的I/O调度层来优化读写。 | 硬盘、SSD、U盘、SD卡。 |
| 网络设备 | 面向数据包。 | 没有设备文件节点,通过套接字接口进行访问。 | 网卡、蓝牙适配器。 |
2. Linux操作系统与驱动的关系
简单来说:内核是管理者,驱动是执行者。
-
Linux内核:像一个政府的核心内阁。它制定法律和标准(接口和规则),负责管理国家核心资源(CPU、内存、进程),并设立各个部门(子系统)来管理不同事务。
-
设备驱动:像各个政府部门的执行机构(例如,交通部下属的交警支队、铁路局)。它们专门负责与特定的对象(硬件设备)打交道,执行内阁制定的标准,并向内阁汇报。

用户空间包括应用程序和系统调用两层。应用程序一般依赖于函数库,而函数库是由系统调用来编写的,所以应用程序间接地依赖于系统调用。
系统调用层是内核空间和用户空间的接口层。通过这个系统调用层,应用程序不需要直接访问内核空间的程序,增加了内核的安全性。同时,应用程序也不能访问硬件设备,只能通过系统调用层来访问硬件设备。如果应用程序需要访问硬件设备,那么应用程序先访问系统调用层,由系统调用层去访问内核层的设备驱动程序。这样的设计,保证了各个模块的功能独立性,也保证了系统的安全。
系统调用层依赖内核空间的各个模块来实现。在Linux内核中,包含很多实现具体功能的模块。这些模块包括文件系统、网络协议栈、设备驱动、内核调度、内存管理、进程管理等,都属于系统内核空间。
最底层是硬件层,这一层是实际硬件设备的抽象。设备驱动程序的功能就是驱动这一层硬件。设备驱动程序可以工作在有操作系统的情况下,也可以工作在没有操作系统的情况下。如果只需要实现一些简单的控制设备的操作,那么可以不使用操作系统。如果嵌入式系统完成的功能比较复杂,则往往需要操作系统来帮忙。大多数操作系统都具有多任务的特性,所以对于设备驱动程序来说,应该充分考虑并发、阻塞等问题。
举一个例子,一个完整的I/O请求(如write)如何穿越应用程序、内核、驱动最终到达硬件:

3. Lnux驱动程序开发
3.1 用户态和内核态
Linux操作系统分为用户态和内核态。
- 用户态:处理上层的软件工作。
- 内核态:用来管理用户态的程序,完成用户态请求的工作。
Linux操作系统分为两个状态的原因主要是,为应用程序提供一个统一的计算机硬件抽象。工作在用户态的应用程序完全可以不考虑底层的硬件操作,这些操作由内核态程序来完成。这些内核态程序大部分是设备驱动程序。一个好的操作系统的驱动程序对用户态应用程序应该是透明的,也就是说,应用程序可以在不了解硬件工作原理的情况下,很好地操作硬件设备,同时不会使硬件设备进入非法状态。Linux操作系统很好的做到了这一点。
| 特性 | 用户态 (User Mode) | 内核态 (Kernel Mode) |
|---|---|---|
| CPU特权级 | 低(如x86的Ring 3) | 高(如x86的Ring 0) |
| 内存访问 | 只能访问属于自己的虚拟内存空间 | 可以访问整个系统的所有物理内存 |
| 指令执行 | 只能执行非特权指令 | 可以执行所有指令,包括特权指令(如操作硬件寄存器) |
| 崩溃影响 | 只导致自身进程崩溃 | 导致整个系统崩溃(内核恐慌) |
3.2 模块机制
模块是可以在运行时加入内核的代码,这是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);/*指定模块的卸载函数的宏*/



被折叠的 条评论
为什么被折叠?



