【LINUX驱动学习】LINUX驱动理论

其实很早之前就已经学习了杂项设备驱动的编写,但是之前一直觉得模模糊糊,还是有一些不太理解的地方。最近在看完周立功的一本书后有所启发,开始写一些关于驱动的东西。
(一)驱动设备分类
Linux 系统中的设备可以分为字符设备、块设备和网络设备这 3 类。
字符设备:字符设备是能够像字节流一样被访问的设备,当对字符设备发出读写请求,相应的 I/O 操作立即发生。Linux 系统中很多设备都是字符设备,如字符终端、串口、键盘、鼠标等。在嵌入式 Linux 开发中,接触最多的就是字符设备以及驱动。
块设备:块设备是 Linux 系统中进行 I/O 操作时必须以块为单位进行访问的设备,块设备能够安装文件系统。块设备驱动会利用一块系统内存作为缓冲区,因此对块设备发出读写访问,并不一定立即产生硬件 I/O 操作。Linux 系统中常见的块设备有如硬盘、软驱等等。
网络设备:网络设备既可以是网卡这样的硬件设备,也可以是一个纯软件设备如回环设备。网络设备由 Linux 的网络子系统驱动,负责数据包的发送和接收,而不是面向流设备,因此在Linux系统文件系统中网络设备没有节点。对网络设备的访问是通过socket调用产生,而不是普通的文件操作如 open/close 和 read/write 等
其实我们平常最常用的应该也就是字符类的设备。
(二)设备节点和设备号
1. 设备节点
设备(包括硬件设备)在 Linux 系统下,表现为设备节点,也称设备文件。设备文件是一种特殊的文件,它们存储在文件系统中(通常在/dev 目录下),但它们仅占用文件目录项而不涉及存储数据。事实上,它们仅仅记录了其所属的设备类别、主设备号和从设备号等设备相关信息。
来看两个典型的设备文件的详细信息:
chenxibing@gitserver-zhiyuan:~$ ls -l /dev/ttyS0 /dev/sda1
brw-rw---- 1 root disk 8, 1 2011-01-07 17:48 /dev/sda1
crw-rw---- 1 root dialout 4, 64 2011-01-07 17:48 /dev/ttyS0

设备分为字符设备、块设备和网络设备,而网络设备没有设备节点,所以设备文件基本上就分为字符设备文件和块设备文件两类,在设备节点属性中,分别以 c 和 b 来表示,即 c表示字符设备节点文件,b 表示块设备节点文件。
当程序打开一个设备文件时,内核就可以获取对应设备的设备类型、主设备号和次设备号等信息,内核也就知道了程序需要操作使用哪个设备驱动程序。在程序随后对这个文件的操作都会调用相应的驱动程序的函数,同时把从设备号传递给驱动程序

2.设备编号
设备编号由主设备号和从设备号构成。在 Linux 内核中,使用 dev_t 类型来保存设备编号。在 2.6 版本的 Linux 内核中,dev_t 是一个 32 位数,高 12 位是主设备号,低 20 位是次设备号。
主设备号标识设备对应的驱动程序,告诉 Linux 内核使用哪个驱动程序驱动该设备。如果多个设备使用同一个驱动程序,则它们拥有相同的主设备号。例如/dev/ttyS0~3 这 4 个设备,拥有相同的主设备号 4,说明它们使用同一份驱动:
chenxibing@gitserver-zhiyuan:~$ ls -l /dev/ttyS*
crw-rw---- 1 root dialout 4, 64 2011-01-07 17:48 /dev/ttyS0
crw-rw---- 1 root dialout 4, 65 2011-01-07 17:48 /dev/ttyS1
crw-rw---- 1 root dialout 4, 66 2011-01-07 17:48 /dev/ttyS2
crw-rw---- 1 root dialout 4, 67 2011-01-07 17:48 /dev/ttyS3
主设备号由系统来维护,尽管 2.6 Linux 可以容纳大量的设备,但是在使用主设备号的时候,注意一定不要使用系统已经使用的主设备号。一般来说,231~239 这几个设备号是系统没有分配的,用户可以自行安排使用。当前运行系统占用了哪些主设备号,可通过查看/proc/devices 文件得到。

3. 获取和释放设备编号
在建立一个设备节点之前,驱动程序首先应当为这个设备获得一个可用的设备号,注销设备需要释放所占用的设备号。设备号的生命周期是从设备注册到设备注销,在此期间,所占用的设备号不能被其它驱动使用。Linux 内核支持静态获取和动态获取设备号,下面以字符设备为例讲述设备号的获取与释放。
(1 )静态获取主设备号
静态设备号的方式适用于下列情况:
1) 该驱动只在特定系统运行,且系统设备号使用情况明确;
2) 系统应用所要求;如为了快速启动等。

如果要从系统获得几个或者几个既定的主设备号,可用 register_chrdev_region 函数来获取。该函数在<linux/fs.h>中声明,函数定义如下:
int register_chrdev_region(dev_t first,unsigned int count,char *name);
这个函数可以向系统注册 1 个或者多个主设备号,first 是起始编号,count 是主设备号的数量,name 则是设备名称。注册成功返回 0,否则返回错误码。

(2 )动态获取主设备号
如果事先不知道设备的设备号,或者一个驱动可能在多个系统上运行,为了避免出现设备号冲突,必须采用动态设备号。调用 alloc_chrdev_region 函数可以从系统获得一个或者多
个主设备号。
alloc_chrdev_region 函数在<linux/fs.h>中定义:
alloc_chrdev_region(dev_t *dev, unsigned int firstminor,unsigned int count, char *name);
alloc_chrdev_region 函数可以从系统动态获得一个或者多个主设备号。dev 用于保存已经获得的编号范围的第一个值,firstminor 是第一个次设备号,通常是 0,count 是获得的编号数量,name 是设备名称。动态获取得到的设备号,一定要用一个全局变量保存下来,以便卸载使用,否则该设备号将不能被释放。程序清单 2.7 是一个动态获取设备号的使用范例。

(3 )释放设备号
在设备注销的时候必须释放占用的主设备号,调unregister_chrdev_region 可以释放设
备号。函数原型:
void unregister_chrdev_region(dev_t from, unsigned count);

(三)设备与驱动关系
在这里插入图片描述驱动程序运行与内核空间,用户程序只能通过内核提供的系统调用,由经 VFS 以及驱动程序才能访问和操作硬件,硬件设备传递的数据也必须经过驱动、VFS 和系统调用才能被用户程序接收。所以说,设备驱动是应用程序访问系统设备以及进行数据传递的桥梁和通道。

  1. 驱动的基本要素

在这里插入图片描述
Linux 设备在内核中是用设备号进行区分的,而决定这些设备号的正是设备驱动程序。另外,在用户空间如何管理这些设备,这也是与驱动程序息息相关的。一个完整的设备驱动必须具备以下基本要素:

  1. 驱动的入口和出口。驱动入口和出口部分的代码,并不与应用程序直接交互,仅仅只与内核模块管理子系统有交互。在加载内核的时候执行入口代码,卸载的时候执行出口代码。这部分代码与内核版本关系较大,严重依赖于驱动子系统的架构和实现。
  2. 操作设备的各种方法。驱动程序实现了各种用于系统服务的各种方法,但是这些方法并不能主动执行,发挥相应的功能,只能被动的等待应用程序的系统调用,只有经过相应的系统调用,各方法才能发挥相应的功能,如应用程序执行 read()系统调用,内核才能执行驱动 xxx_read()方法的代码。这部分代码主要与硬件和所需要实现的操作相关。
  3. 提供设备管理方法支持。包括设备号的分配和设备的注册等。这部分代码与内核版本以及最终所采用的设备管理方法相关系,如采用 udev,则驱动必须提供相应的支持代码。

2.驱动程序和应用程序区别
驱动程序与普通应用程序有很大不同,主要表现在以下 3 个方面:
在程序组成和逻辑方面,普通应用程序一般都是由始至终完成某个任务,而驱动程序内部各方法之间相互独立,没有逻辑联系。
在系统资源访问方面,内核模块运行在内核态,可以操作系统的任何资源,包括硬件,但是应用程序却不能直接访问系统硬件,只有借助驱动程序才能访问硬件。
在出错危害性方面,应用程序出错或者崩溃一般不会引起内核崩溃,可以通过杀死程序进程终止,但是内核模块出错,有可能导致内核崩溃,一旦内核崩溃,只能复位系统。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

与光同程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值