实验七:Linux块设备驱动

整理文档很辛苦,赏杯茶钱您下走!

免费阅读已结束,点击下载阅读编辑剩下 ...

阅读已结束,您可以下载文档离线阅读编辑

资源描述

实验七:Linux块设备驱动块设备是与字符设备并列的概念,这两类设备在Linux中驱动的结构有较大差异,总体而言,块设备驱动比字符设备驱动要复杂得多,在I/O操作上表现出极大的不同,缓冲、I/O调度、请求队列等都是与块设备驱动相关的概念。本章将详细讲解Linux块设备驱动的编程方法。1.块设备的I/O操作特点字符设备与块设备I/O操作的不同如下:(1)块设备只能以块为单位接受输入和返回输出,而字符设备则以字节为单位。大多数设备是字符设备,因为它们不需要缓冲而且不以固定块大小进行操作。(2)块设备对于I/O请求有对应的缓冲区,因此它们可以选择以什么顺序进行响应,字符设备无须缓冲且被直接读写。对于存储设备而言调整读写的顺序作用巨大,因为在读写连续的扇区比分离的扇区更快。(3)字符设备只能被顺序读写,而块设备可以随机访问。虽然块设备可随机访问,但是对于磁盘这类机械设备而言,顺序地组织块设备的访问可以提高性能。而对SD卡、RamDisk(RamDisk是通过使用软件将RAM模拟当做硬盘来使用的一种技术)等块设备而言,不存在机械上的原因,进行这样的调整没有必要。2.Linux块设备驱动结构2.1.block_device_operations结构体在块设备驱动中,有一个类似于字符设备驱动中file_operations结构体的block_device_operations结构体,它是对块设备操作的集合,定义如代码清单1所示。代码清单1block_device_operations结构体structblock_device_operations{int(*open)(structinode*,structfile*);//打开int(*release)(structinode*,structfile*);//释放int(*ioctl)(structinode*,structfile*,unsigned,unsignedlong);//ioctllong(*unlocked_ioctl)(structfile*,unsigned,unsignedlong);long(*compat_ioctl)(structfile*,unsigned,unsignedlong);int(*direct_access)(structblock_device*,sector_t,unsignedlong*);int(*media_changed)(structgendisk*);//介质被改变int(*revalidate_disk)(structgendisk*);//使介质有效int(*getgeo)(structblock_device*,structhd_geometry*);//填充驱动器信息structmodule*owner;//模块拥有者};下面对其主要的成员函数进行分析。1.打开和释放int(*open)(structinode*inode,structfile*filp);int(*release)(structinode*inode,structfile*filp);与字符设备驱动类似,当设备被打开和关闭时将调用它们。2.IO控制int(*ioctl)(structinode*inode,structfile*filp,unsignedintcmd,unsignedlongarg);上述函数是ioctl()系统调用的实现,块设备包含大量的标准请求,这些标准请求由Linux块设备层处理,因此大部分块设备驱动的ioctl()函数相当短。3.介质改变int(*media_changed)(structgendisk*gd);被内核调用来检查是否驱动器中的介质已经改变,如果是,则返回一个非0值,否则返回0。这个函数仅适用于支持可移动介质的驱动器,通常需要在驱动中增加一个表示介质状态是否改变的标志变量,非可移动设备的驱动不需要实现这个方法。4.使介质有效int(*revalidate_disk)(structgendisk*gd);revalidate_disk()被调用来响应一个介质改变,它给驱动一个机会来进行必要的工作以使新介质准备好。5.获得驱动器信息int(*getgeo)(structblock_device*,structhd_geometry*);根据驱动器的几何信息填充一个hd_geometry结构体,hd_geometry结构体包含磁头、扇区、柱面等信息。6.模块指针structmodule*owner;一个指向拥有这个结构体的模块的指针,它通常被初始化为THIS_MODULE。2.2.gendisk结构体在Linux内核中,使用gendisk(通用磁盘)结构体来表示1个独立的磁盘设备(或分区),这个结构体的定义如代码清单2所示。代码清单2gendisk结构体structgendisk{intmajor;/*主设备号*/intfirst_minor;/*第1个次设备号*/intminors;/*最大的次设备数,如果不能分区,则为1*/chardisk_name[32];/*设备名称*/structhd_struct**part;/*磁盘上的分区信息*/structblock_device_operations*fops;/*块设备操作结构体*/structrequest_queue*queue;/*请求队列*/void*private_data;/*私有数据*/sector_tcapacity;/*扇区数,512字节为1个扇区*/intflags;chardevfs_name[64];intnumber;structdevice*driverfs_dev;structkobjectkobj;structtimer_rand_state*random;intpolicy;atomic_tsync_io;/*RAID*/unsignedlongstamp;intin_flight;#ifdefCONFIG_SMPstructdisk_stats*dkstats;#elsestructdisk_statsdkstats;#endif};major、first_minor和minors共同表征了磁盘的主、次设备号,同一个磁盘的各个分区共享一个主设备号,而次设备号则不同。fops为block_device_operations,即上节描述的块设备操作集合。queue是内核用来管理这个设备的I/O请求队列的指针。capacity表明设备的容量,以512个字节为单位。private_data可用于指向磁盘的任何私有数据,用法与字符设备驱动file结构体的private_data类似。Linux内核提供了一组函数来操作gendisk,如下所示:1.分配gendiskgendisk结构体是一个动态分配的结构体,它需要特别的内核操作来初始化,驱动不能自己分配这个结构体,而应该使用下列函数来分配gendisk:structgendisk*alloc_disk(intminors);minors参数是这个磁盘使用的次设备号的数量,一般也就是磁盘分区的数量,此后minors不能被修改。2.增加gendiskgendisk结构体被分配之后,系统还不能使用这个磁盘,需要调用如下函数来注册这个磁盘设备。voidadd_disk(structgendisk*gd);特别要注意的是对add_disk()的调用必须发生在驱动程序的初始化工作完成并能响应磁盘的请求之后。3.释放gendisk当不再需要一个磁盘时,应当使用如下函数释放gendisk。voiddel_gendisk(structgendisk*gd);4.设置gendisk容量voidset_capacity(structgendisk*disk,sector_tsize);块设备中最小的可寻址单元是扇区,扇区大小一般是2的整数倍,最常见的大小是512字节。扇区的大小是设备的物理属性,扇区是所有块设备的基本单元,块设备无法对比它还小的单元进行寻址和操作,不过许多块设备能够一次就传输多个扇区。虽然大多数块设备的扇区大小都是512字节,不过其他大小的扇区也很常见,比如,很多CD-ROM盘的扇区都是2KB。不管物理设备的真实扇区大小是多少,内核与块设备驱动交互的扇区都以512字节为单位。因此,set_capacity()函数也以512字节为单位。2.3.请求结构体request在Linux块设备驱动中,使用request结构体来表征等待进行的I/O请求,这个结构体的定义如代码清单3所示。代码清单3request结构体structrequest{structlist_headqueuelist;/*链表结构*/unsignedlongflags;/*REQ_*/sector_tsector;/*要传送输的下一个扇区*/unsignedlongnr_sectors;/*要传送的扇区数目*/unsignedintcurrent_nr_sectors;/*当前要传送的扇区数目*/sector_thard_sector;/*要完成的下一个扇区*/unsignedlonghard_nr_sectors;/*要被完成的扇区数目*/unsignedinthard_cur_sectors;/*当前要被完成的扇区数目*/structbio*bio;/*请求的bio结构体的链表*/structbio*biotail;/*请求的bio结构体的链表尾*/void*elevator_private;unsignedshortioprio;intrq_status;structgendisk*rq_disk;interrors;unsignedlongstart_time;unsignedshortnr_phys_segments;/*请求在物理内存中占据的不连续的段的数目,scatter/gather列表的尺寸*/unsignedshortnr_hw_segments;/*与nr_phys_segments相同,但考虑了系统I/OMMU的remap*/inttag;char*buffer;/*传送的缓冲,内核虚拟地址*/intref_count;/*引用计数*/...};request结构体的主要成员包括:sector_thard_sector;unsignedlonghard_nr_sectors;unsignedinthard_cur_sectors;上述3个成员标识还未完成的扇区,hard_sector是第一个尚未传输的扇区,hard_nr_sectors是尚待完成的扇区数,hard_cur_sectors是当前I/O操作中待完成的扇区数。这些成员只用于内核块设备层,驱动不应当使用它们,如下所示:sector_tsector;unsignedlongnr_sectors;unsignedintcurrent_nr_sectors;驱动中会经常与这3个成员打交道,这3个成员在内核和驱动交互中发挥着重大作用。它们以512字节大小为一个扇区,如果硬件的扇区大小不是512字节,则需要进行相应的调整。例如,如果硬件的扇区大小是2048字节,则在进行硬件操作之前,需要用4来除起始扇区号。hard_sector、hard_nr_sectors、hard_cur_sectors与sector、nr_sectors、current_nr_sectors之间可认为是“副本”关系。2.4.请求队列结构体request_queue一个块请求队列是一个块I/O请求的队列,其定义如代码清单4。代码清单4request队列结构体structrequest_queue{.../*保护队列结构体的自旋锁*/spinlock_t__queue_lock;spinlock_t*queue_lock;/*队列kobject*/structkobjectkobj;/*队列设置*/unsignedlongnr_requests;/*最大的请求数量*/unsi

1 / 14
下载文档,编辑使用

©2015-2020 m.777doc.com 三七文档.

备案号:鲁ICP备2024069028号-1 客服联系 QQ:2149211541

×
保存成功