设备管理是操作系统五大管理中最复杂的部分。与Unix系统一样,Linux系统采用设备文件统一管理硬件设备,从而将硬件设备的特性及管理细节对用户隐藏起来,实现用户程序与设备无关性。在Linux系统中,硬件设备分为两种,即块设备和字符设备。
1.特别文件
用户是通过文件系统与设备接口的,所有设备都作为特别文件,从而在管理上就具有一些共性。
(1)每个设备都对应文件系统中的一个索引节点,都有一个文件名。设备的文件名一般由两部分构成,第一部分是主设备号,第二部分是次设备号。
主设备号代表设备的类型,可以惟一地确定设备的驱动程序和界面,如hd表示IDE硬盘,sd表示SCSI硬盘,tty表示终端设备等;次设备号代表同类设备中的序号,如hda表示IDE主硬盘,hdb表示IDE从硬盘等。
(2)应用程序通常可以通过系统调用open( )打开设备文件,建立起与目标设备的连接。
(3)对设备的使用类似于对文件的存取。打开设备文件以后,就可以通过read( )、write( )、ioctl( )等文件操作对目标设备进行操作。
(4)设备驱动程序都是系统内核的一部分,它们必须为系统内核或它们的子系统提供一个标准的接口。例如,一个终端驱动程序必须为Linux内核提供一个文件I/O接口;一个SCSI设备驱动程序应该为SCSI子系统提供一个SCSI设备接口,同时SCSI子系统也应为内核提供文件I/O和缓冲区。
(5)设备驱动程序利用一些标准的内核服务,如内存分配等。另外,大多数Linux设备驱动程序都可以在需要时装入内核,不需要时可以卸载下来。
处于应用层的进程通过文件描述字fd与已打开文件的file结构相联系。在文件系统层,按照文件系统的操作规则对该文件进行相应处理。
对于一般文件(即磁盘文件),要进行空间的映射—从普通文件的逻辑空间映射到设备的逻辑空间,然后在设备驱动层做进一步映射—从设备的逻辑空间映射到物理空间(即设备的物理地址空间),进而驱动底层物理设备工作。
对于设备文件,则文件的逻辑空间通常就等价于设备的逻辑空间,然后从设备的逻辑空间映射到设备的物理空间,再驱动底层的物理设备工作。 2.设备驱动程序和内核之间的接口
Linux系统和设备驱动程序之间使用标准的交互接口。无论是字符设备、块设备还是网络设备的驱动程序,当内核请求它们提供服务时,都使用同样的接口。
Linux提供了一种全新的机制,就是“可安装模块”。可安装模块是可以在系统运行时动态地安装和拆卸的内核模块。利用这个机制,可以根据需要在不必对内核重新编译连接的条件下,将可安装模块动态插入运行中的内核,成为其中一个有机组成部分,或者从内核卸载已安装的模块。设备驱动程序或与设备驱动紧密相关的部分(如文件系统) 都是利用可安装模块实现的。
在应用程序界面上,利用内核提供的系统调用来实现可安装模块的动态安装和拆卸。但通常情况下,用户是利用系统提供的插入模块工具和移走模块工具来装卸可安装模块。插入模块的工作主要如下:
(1) 打开要安装的模块,把它读到用户空间。这种“模块”就是经过编译但尚未连接的.o文件。
(2) 必须把模块内涉及对外访问的符号(函数名或变量名)连接到内核,即把这些符号在内核映像中的地址填入该模块需要访问这些符号的指令及数据结构中。
(3) 在内核创建一个module数据结构,并申请所需的系统空间。
(4) 最后,把用户空间中完成了连接的模块映像装入内核空间,并在内核中“登记”本模块的有关数据结构(如file_operations结构),其中有指向执行相关操作函数的指针。
如前所述,Linux系统是一个动态的操作系统。用户根据工作中的需要,会对系统中设备重新配置,如安装新的打印机、卸载老式终端等。这样,每当Linux系统内核初启时,它都要对硬件配置进行检测,很有可能会检测到不同的物理设备,就需要不同的驱动程序。
在构建系统内核时,可以使用配置脚本将设备驱动程序包含在系统内核中。在系统启动时对这些驱动程序初始化,它们可能未找到所控制的设备,而另外的设备驱动程序可以在需要时作为内核模块装入到系统内核中。
为了适应设备驱动程序动态连接的特性,设备驱动程序在其初始化时就在系统内核中进行登记。Linux系统利用设备驱动程序的登记表作为内核与驱动程序接口的一部分,这些表中包括指向有关处理程序的指针和其它信息。
3.字符设备
在Linux系统中,打印机、终端等字符设备都作为字符特别文件出现在用户面前。用户对字符设备的使用就和存取普通文件一样。在应用程序中,使用标准的系统调用来打开、关闭、读写字符设备。当字符设备初始化时,其设备驱动程序被添加到由device_struct结构组成的chrdevs结构数组中。
device_struct结构由两项构成,一个是指向已登记的设备驱动程序名的指针,另一个是指向file_operations结构的指针。而file_operations结构的成分几乎全是函数指针,分别指向实现文件操作的入口函数。设备的主设备号用来对chrdevs数组进行索引。
前面讲过,每个VFS索引节点都和一系列文件操作相联系,并且这些文件操作随索引节点所代表的文件类型不同而不同。每当一个VFS索引节点所代表的字符设备文件创建时,它的有关文件的操作就设置为默认的字符设备操作。
默认的文件操作只包含一个打开文件的操作。当打开一个代表字符设备的特别文件以后,就得到相应的VFS索引节点,其中包括该设备的主设备号和次设备号。
利用主设备号就可以检索chrdevs数组,进而可以找到有关此设备的各种文件操作。这样,应用程序中的文件操作就会映射到字符设备的文件操作调用中。
4.块设备
对块设备的存取和对文件的存取方式一样,其实现机制也和字符设备使用的机制相同。Linux系统中有一个名为blkdevs的结构数组,它描述了一系列在系统中登记的块设备。
数组blkdevs也使用设备的主设备号作为索引,其元素类型是device_struct结构。该结构中包括指向已登记的设备驱动程序名的指针和指向block_device_operations结构的指针。
在block_device_operations结构中包含指向有关操作的函数指针。所以,该结构就是连接抽象的块设备操作与具体块设备类型的操作之间的枢纽。
与字符设备不一样,块设备有几种类型,例如SCSI设备和IDE设备。每类块设备都在Linux系统内核中登记,并向内核提供自己的文件操作。
为了把各种块设备的操作请求队列有效地组织起来,内核中设置了一个结构数组blk_dev,该数组中的元素类型是blk_dev_struct结构。这个结构由三个成分组成,其主体是执行操作的请求队列request_queue,还有一个函数指针queue。
当这个指针不为0时,就调用这个函数来找到具体设备的请求队列。这是考虑到多个设备可能具有同一主设备号,该指针在设备初始化时被设置好。通常当它不为0时,还要使用该结构中的另一个指针data,用来提供辅助性信息,帮助该函数找到特定设备的请求队列。每一个请求数据结构都代表一个来自缓冲区的请求。
每当缓冲区要和一个登记过的块设备交换数据,它都会在blk_dev_struct中添加一个请求数据结构.
每一个请求都有一个指针指向一个或多个buffer_head数据结构,而该结构都是一个读写数据块的请求。每一个请求结构都在一个静态链表all_requests中。若干请求是添加到一个空的请求链表中,则调用设备驱动程序的请求函数,开始处理该请求队列。否则,设备驱动程序就简单地处理请求队列中的每一个请求。
当设备驱动程序完成了一个请求后,就把buffer_head结构从request结构中移走,并标记buffer_head结构已更新,同时解锁,这样,就可以唤醒相应的等待进程。
linux系统管理教程
转载请注明出处:服务器评测 » linux系统管理教程