磁盘(disk)是指利用磁记录技术存储数据的存储器。
磁盘是计算机主要的存储介质,可以存储大量的二进制数据,并且断电后也能保持数据不丢失。早期计算机使用的磁盘是软磁盘(Floppy Disk,简称软盘),如今常用的磁盘是硬磁盘(Hard disk,简称硬盘)。
块设备(block device)
是一种具有一定结构的随机存取设备,对这种设备的读写是按块进行的,它使用缓冲区来存放暂时的数据,满足条件,从缓存一次性写入设备或者从设备一次性读到缓冲区。如磁盘、SD卡都是常见的块设备。
a、扇区:硬盘的基本访问单位,扇区的大小一般是 512B
;
b、块:由Linux制定对内核或文件系统等数据处理的基本单位。通常,1个块由1个或多个扇区组成;
c、段:主要为了做 scatter/gather DMA 操作使用,同一个物理页面中的在硬盘存储介质上连续的多个块组成一个段;
d、文件块:大小定义和文件系统块一样;只是相对于文件的一个偏移逻辑块,需要通过具体文件系统中的此文件对应的 inode 所记录的间接块信息,换算成对应的文件系统块;此做法是为了将一个文件的内容存于硬盘的不同位置,以提高访问速度;即一个文件的内容在硬盘是一般是不连续的;EXT2中,ext2_get_block()
完成文件块到文件系统块的映射。
块设备驱动是基于扇区(sector)来访问底层物理磁盘,基于块(block)来访问上层文件系统。
块设备驱动是以何种方式对块设备进行访问的?
在Linux中,驱动对块设备的输入或输出( I/O )操作,都会向块设备发出一个请求,在驱动中用 request
结构体描述。但对于一些磁盘设备而言请求的速度很慢,这时候内核就提供一种队列的机制把这些 I/O 请求添加到队列中(即:请求队列),在驱动中用 request_queue
结构体描述。
在向块设备提交这些请求前内核会先执行请求的合并和排序预操作,以提高访问的效率,然后再由内核中的 I/O 调度程序子系统来负责提交 I/O 请求,I/O 调度程序将磁盘资源分配给系统中所有挂起的块 I/O 请求,其工作是管理块设备的请求队列,决定队列中的请求的排列顺序以及什么时候派发请求。
由通用块层(Generic Block Layer)负责维持一个I/O请求在上层文件系统与底层物理磁盘之间的关系。在通用块层中,通常用一个 bio
结构体来对应一个I/O请求。
Linux 提供了一个 gendisk
数据结构体,用来表示一个独立的磁盘设备或分区,用于对底层物理磁盘进行访问。在gendisk中有一个类似字符设备中file_operations
的硬件操作结构指针,是block_device_operations
结构体。
当多个请求提交给块设备时,执行效率依赖于请求的顺序。如果所有的请求是同一个方向(如:写数据),执行效率是最大的。
内核在调用块设备驱动程序例程处理请求之前,先收集I/O请求并将请求排序,然后,将连续扇区操作的多个请求进行合并以提高执行效率(内核算法会自己做,不用你管),对I/O请求排序的算法称为电梯算法(elevator algorithm)。电梯算法在I/O调度层完成。内核提供了不同类型的电梯算法,电梯算法有
(1)noop(实现简单的FIFO,基本的直接合并与排序),
(2)anticipatory(延迟I/O请求,进行临界区的优化排序),
(3)Deadline(针对anticipatory缺点进行改善,降低延迟时间),
(4)Cfq(均匀分配I/O带宽,公平机制)
其实IO调度层(包括请求合并排序算法)是不需要用户管的,内核已经做好。
块设备I/O操作
- 块设备只能以块为单位接收输入和返回输出,而字符设备则以字节为单位;
- 块设备对于I/O请求有对应的缓冲区,因此它们可以选择以什么顺序进行响应,字符设备无需缓冲且被直接读写。对于存储设备而言,调整读写的顺序作用巨大,因为在读写连续的扇区的存储速度比分离的扇区更快。
- 字符设备只能被顺序读写,而块设备可以随机访问。
块设备
block_device 是伪文件系统 bdevfs 中对块设备或设备分区抽象,它唯一对应于一个设备号(对分区来讲、主设备号相同,次设备号不同)
如果该结构代表一个分区,则其成员 bd_part 指向设备的分区结构。如果该结构代表设备,则其成员bd_disk 指向设备的通用硬盘结构 gendisk。
当用户打开块设备文件时,内核创建结构 block_device 实例,设备驱动程序还将创建结构gendisk 实例,分配请求队列并注册结构 block_device 实例。
通用磁盘
gendisk,对通用磁盘的描述,与真正的底层物理设备相关联。
结构体 gendisk 代表了一个通用硬盘(generic hard disk)对象,它存储了一个硬盘的信息,包括请求队列、分区链表和块设备操作函数集等。块设备驱动程序分配结构 gendisk 实例,装载分区表,分配请求队列并填充结构的其他域。
支持分区的块驱动程序必须包含 <linux/genhd.h> 头文件,并声明一个结构 gendisk,内核还维护该结构实例的一个全局链表 gendisk_head,通过函数a dd_gendisk、del_gendisk 和get_gendisk 维护该链表。
磁盘分区
如果将一个磁盘分成了几个分区,那么其分区表保存在hd_struct
结构的数组中,该数组的地址存放在 gendisk
对象的 part
字段中。通过磁盘内分区的相对索引对该数组进行索引。
块设备操作集合
在 block_device_operations
中没有实际读或写数据的函数。在块 I/O 子系统,这些操作由请求函数处理。
Linux内核之块设备驱动(超级详细~)
【驱动】块设备驱动(二)-通用块层