第8章设备驱动提纲1、Linux驱动程序简介2、设备驱动程序结构3、Linux内核设备模型4、同步机制5、内存映射和管理6、工作队列7、异步I/O8、DMA1、Linux驱动程序简介设备驱动程序是操作系统内核和机器硬件之间的接口设备驱动程序为应用程序屏蔽了硬件的细节,在应用程序看来,硬件设备只是一个设备文件,应用程序可以像操作普通文件一样对硬件设备进行操作Linux驱动程序简介设备驱动程序是内核的一部分,它完成以下的功能:◦(1)对设备的初始化和释放。◦(2)把数据从内核传送到硬件和从硬件读取数据到内核。◦(3)读取应用程序传送给设备文件的数据和回送应用程序请求的数据。这需要在用户空间,内核空间,总线以及外设之间传输数据。◦(4)检测和处理设备出现的错误。Linux驱动程序简介设备的分类◦字符设备无需缓冲直接读写的设备,如系统的串口设备/dev/cua0和/dev/cua1◦块设备以块为单位进行读写,典型的块大小为512或1024字节;块设备的存取是通过buffer、cache来进行并且可以随机访问,即不管块位于设备中何处都可以对其进行读写◦网络设备通过BSD套接口访问Linux驱动程序简介设备文件◦Linux抽象了对硬件的处理,所有的硬件设备都可以作为普通文件一样来看待:它们可以使用和操作文件相同的、标准的系统调用接口来完成打开、关闭、读写和I/O控制操作,而驱动程序的主要任务也就是要实现这些系统调用函数◦Linux为文件和设备提供了一致的用户接口。对用户来说,设备文件与普通文件并无区别Linux驱动程序简介主设备号和次设备号◦主设备号标识该设备的种类,也标识了该设备所使用的驱动程序◦次设备号标识使用同一设备驱动程序的不同硬件设备Linux驱动程序简介Linux设备驱动代码的分布◦所有Linux的设备驱动源码都放在drivers目录中,分成以下几类:block:块设备驱动包括IDE(在ide.c中)驱动。块设备包括IDE与SCSI设备。char:包含字符设备的驱动,如ttys、串行口以及鼠标等等Linux驱动程序简介Linux设备驱动程序的特点◦(1)内核代码◦(2)内核接口◦(3)内核机制与服务◦(4)可加载◦(5)可配置◦(6)动态性2、设备驱动程序结构Linux的设备驱动程序与外界的接口可以分成三部分:◦(1)驱动程序与操作系统内核的接口◦(2)驱动程序与系统引导的接口◦(3)驱动程序与设备的接口设备驱动程序结构驱动程序的注册与注销◦向系统增加一个驱动程序意味着要赋予它一个主设备号,这可以通过在驱动程序的初始化过程中调用定义在fs/devices.c中的register_chrdev()函数或者fs/block_dev.c中的register_blkdev()函数来完成。而在关闭字符设备或者块设备时,则需要通过调用unregister_chrdev()或unregister_blkdev()函数从内核中注销设备,同时释放占用的主设备号。设备驱动程序结构设备的打开与释放◦打开设备是通过调用定义在include/linux/fs.h中的file_operations结构中的函数open()来完成◦释放设备是通过调用file_operations结构中的函数release()来完成设备驱动程序结构设备的读写操作◦字符设备的读写操作相对比较简单,直接使用函数read()和write()就可以了◦块设备的话,则需要调用函数block_read()和block_write()来进行数据读写设备驱动程序结构设备的控制操作◦通过设备驱动程序中的函数ioctl()来完成设备驱动程序结构设备的轮询和中断处理◦设备执行某个命令时,如“将读取磁头移动到软盘的第42扇区上”,设备驱动可以从轮询方式和中断方式中选择一种以判断设备是否已经完成此命令。◦不支持中断的硬件设备,读写时需要轮流查询设备状态3、Linux内核设备模型内核设备模型是Linux2.6之后引进的,是为了适应系统拓扑结构越来越复杂,对电源管理、热插拔支持要求越来越高等形势下开发的全新的设备模型。它采用sysfs文件系统,一个类似于/proc文件系统的特殊文件系统,作用是将系统中的设备组织成层次结构,然后向用户程序提供内核数据结构信息。Linux内核设备模型设备模型建立的目的◦代码重复最小;◦提供如引用计数这样的统一机制;◦例举系统中所有设备,观察其状态,查看其连接总线;◦用树的形式将全部设备结构完整、有效地展现,包括所有总线和内部连接;◦将设备和对应驱动联系起来;◦将设备按照类型分类;◦从树的叶子向根的方向依次遍历,确保以正确顺序关闭各个设备的电源Linux内核设备模型sysfs—设备拓扑结构的文件系统表现/sys|--block|--bus|--class|--dev|--devices|--firmware|--fs|--kernel|--module`--powerLinux内核设备模型sysfs根目录下有10个目录,分别是:block,bus,class,dev,devices,firmware,fs,kernel,module和power◦Block目录:其下的每个子目录分别对应系统中的一个块设备,每个目录又都包含该块设备的所有分区;◦class目录:包含以高层功能逻辑组织起来的系统设备视图;◦等等Linux内核设备模型驱动模型和sysfs◦Linux2.6设备驱动模型的基本元素是设备类结构classes、总线结构bus、设备结构devices、驱动结构drivers◦Linux统一设备模型的基本结构类型说明对应内核数据结构对应/sys项总线类型(BusTypes)系统中用于连接设备的总线structbus_type/sys/bus/*/设备(Devices)内核识别的所有设备,依照连接它们的总线进行组织structdevice/sys/devices/*/*/../设备类别(DeviceClasses)系统中设备的类型(声卡,网卡,显卡,输入设备等),同一类中包含的设备可能连接不同的总线structclass/sys/class/*/设备驱动(DeviceDrivers)在一个系统中安装多个相同设备,只需要一份驱动程序的支持structdevice_driver/sys/bus/pic/drivers/*/Linux内核设备模型Kobject:kobject的主要功能之一是提供一个统一的计数系统。由于kobject是“基”对象,其他对象,如device,bus,class,device_driver等容器都会将其包含,其他对象的引用计数继承或封装kobject的引用计数就可以了。structkobject{constchar*name;/*短名字*/structkobject*parent;/*表示对象的层次关系*/structsysfs_dirent*sd;/*表示sysfs中的一个目录项*/};Linux内核设备模型结构体kref:深入到引用计数系统的内部,可以发现kobject的引用计数是通过kref结构体实现,其定义在头文件linux/kref.h中:structkref{atomic_trefcount;};Linux内核设备模型结构体ktype:kobject是一个抽象且基本的对象,对于一族具有共同特性的kobject,就要用ktype描述。structkobj_type{void(*release)(structkobject*kobj);structsysfs_ops*sysfs_ops;structattribute**default_attrs;}指针指向对象说明release析构函数当kobject引用计数减至0时,调用这个析构函数。作用是释放所有kobject使用的内存和做相关清理工作。sysfs_opssysfs_ops结构体sysfs_ops结构体包含两个函数:对属性进行操作的读写函数show()和store()。default_attrsattribute结构体数组这些结构定义了kobject相关的默认属性。属性描述了给定对象的特征;属性对应/sys树形结构中的叶子节点,就是文件。Linux内核设备模型结构体kset:kset,是kobject对象的集合体,可以看作一个容器,把所有相关的kobject聚集起来structkset{structlist_headlist;spinlock_tlist_lock;structkobjectkobj;structkset_uevent_ops*uevent_ops;}Linux内核设备模型platform总线:platform总线是Linux内核中的一个虚拟总线,使设备的管理更加简单化。目前,大部分的驱动都是用platform总线来写的4、同步机制在操作系统中,多个内核执行流会在同一时间执行,所以和多进程多线程编程一样,内核也需要一些同步机制来同步各执行单元对共享数据的访问。特别的,在多处理器系统上,更需要一些同步机制来同步不同处理器上的执行单元对共享数据的访问。在Linux内核中,包含了几乎所有主流操作系统具有的同步机制,由于本文采用的是2.6内核,它包括:同步锁、信号量、原子操作和完成事件。同步机制同步锁◦自旋锁(Spinlock)◦读写锁(rmlock)◦RCU锁◦Seqlock同步机制信号量◦Linux内核的信号量在概念和原理上与用户态的IPC机制信号量是一样的,但是不能用在内核之外,它是一种睡眠锁◦当一个任务试图获得已被占用的信号量时,会进入一个等待队列,然后睡眠◦当持有该信号量的进程释放信号量后,位于等待队列的一个任务就会被唤醒,这个任务获得信号量同步机制读写信号量◦在应用读写信号量的场景中,访问者被细分为两类,一种是读者,另一种是写者◦读者在拥有读写信号量期间,对该读写信号量保护的共享资源只能进行读访问◦如果某个任务同时需要读和写,则被归类为写者,它在对共享资源访问之前须先获得写者身份,写者在不需要写访问的情况下将被降级为读者同步机制原子操作◦原子操作是指该操作在执行完毕前绝不会被任何其他任务或时间打断,换句话说,它是最小的执行单位,不会有比它更小的执行单位◦原子的概念使用的是物理学里的物质微粒的概念同步机制完成事件(completion)◦完成事件是一种简单的同步机制,表示“thingsmayproceed”,它适用于需要睡眠和唤醒的情景◦如果要在任务中实现简单睡眠直到其它进程完成某些处理过程为止,可以采用完成事件,它不会引起资源竞争◦如果要使用completion,需要包含linux/completion.h,同时创建类型为structcompletion的变量同步机制时间◦测量时间流失(jiffies)◦获知当前时间◦延后执行◦内核定时器5、内存映射和管理物理地址映射到虚拟地址◦每一种外设的访问都是通过读写设备上的寄存器来进行,包括控制寄存器、状态寄存器和数据寄存器三大类◦CPU对I/O端口的编址方式有两种I/O映射方式内存映射方式内存映射和管理内核空间映射到用户空间◦内存映射是现代Unix最有趣的特性之一。对于驱动来说,内存映射可用来提供用户程序对设备内存的直接存取。◦如果想在用户空间访问内核地址,可以采用mmap方法。用户空间的应用程序通过映射可以直接访问设备的I/O存储区或DMA缓冲。映射一个设备是指关联一些用户空间地址到设备内存。这样,无论何时程序在给定范围内读写,实际上是在存取设备。6、工作队列工作队列(workqueue)是linux内核将工作退后执行的机制tasklets不同的是工作队列把推后的工作交给内核线程去执行工作队列的优势就是允许重新调度甚至睡眠2.6内核开始引入工作队列7、异步I/OLinux的异步I/O(AIO)在版本linux2.5版本内核中首次出现。首先来看一下Linux的I/O机制经历的几个阶段:◦(1)同步