1 块设备
特点:
1)能够随机访问数据
2)最小的可寻址单元是扇区,扇区的大小是2的整数倍,一般是 512字节。
扇区和块:
扇区——物理设备的最小可寻址单元
块——逻辑上的最小寻址单元
2 缓冲区头
调入块到缓冲区中,每个缓存区对应一个buffer_head描述符,称作缓冲区头。
弊端:
1)对内核来说,操作页面更为简单
2)每个缓冲区头只能表示一个块,所以内核在处理大数据时,会分解为对一个个小的块的操作,造成不必要的负担和空间浪费。
3 bio
块I/O的基本容器——bio。
struct bio {
sector_t bi_sector; /* device address in 512 byte
sectors */
struct bio *bi_next; /* request queue link */
struct block_device *bi_bdev;
unsigned long bi_flags; /* status, command, etc */
unsigned long bi_rw; /* bottom bits READ/WRITE,
* top bits priority
*/
unsigned short bi_vcnt; /* how many bio_vec's */
unsigned short bi_idx; /* current index into bvl_vec */
/* Number of segments in this BIO after
* physical address coalescing is performed.
*/
unsigned int bi_phys_segments;
unsigned int bi_size; /* residual I/O count */
/*
* To keep track of the max segment size, we account for the
* sizes of the first and last mergeable segments in this bio.
*/
unsigned int bi_seg_front_size;
unsigned int bi_seg_back_size;
unsigned int bi_max_vecs; /* max bvl_vecs we can hold */
unsigned int bi_comp_cpu; /* completion CPU */
atomic_t bi_cnt; /* pin count */
struct bio_vec *bi_io_vec; /* the actual vec list */
bio_end_io_t *bi_end_io;
void *bi_private;
#if defined(CONFIG_BLK_DEV_INTEGRITY)
struct bio_integrity_payload *bi_integrity; /* data integrity */
#endif
bio_destructor_t *bi_destructor; /* destructor */
/*
* We can inline a number of vecs at the end of the bio, to avoid
* double allocations for a small number of bio_vecs. This member
* MUST obviously be kept at the very end of the bio.
*/
struct bio_vec bi_inline_vecs[0];
};
最重要的是bi_vec、bi_vcnt和bi_idx,关系
每个IO请求通过一个bio结构体描述,每个请求包括一个或多个块。
bi_io_vect指向bio_vec结构体数组,该结构体数组包含了一个特定I/O操作所需使用到的所有片段,bio_vec描述了每个片段在物理页中的实际位置。共有bi_cnt个片段,bi_idx指向当前片段。
4 I/O调度
为减少磁盘寻址时间,需要对块IO请求做优化。通过两种方法:合并与排序。
4.1 linus 电梯
按扇区增长方向有序排列,类似电梯的工作方式,先沿一个方向移动,再向另一方向移动。当一个新请求加入I/O请求队列时,可能会发生以下4种操作:
1)如果队列中已存在一个对相邻磁盘扇区操作的请求,那么新请求将和这个已存在的请求合并成一个请求
2)如果队列中存在一个驻留时间过长的请求,那么新请求之间查到队列尾部,防止旧的请求发生饥饿
3)如果队列中已扇区方向为序存在合适的插入位置,那么新请求将被插入该位置,保证队列中的请求是以被访问磁盘物理位置为序进行排列的
4)如果队列中不存在合适的请求插入位置,请求将被插入到队列尾部
4.2 最终期限I/O调度
linus电梯问题:请求饥饿,发现有驻留时间过长的请求,新请求被插入到队列尾。还有可能造成写—饥饿—读这种特殊问题。
最终期限I/O调度算法给每个请求设置了超时时间,默认情况下,读请求的超时时间500ms,写请求的超时时间是5s
具体过程:
1)新请求交给排序队列
2)将读请求加入到读请求FIFO队列,写请求加入到写请求FIFO队列
3)普通操作,将排序队列的头部推入到派发队列
4)若读/写请求队列有超时,处理超时请求
4.3 预测I/O调度
最终期限I/O调度问题:写操作繁重的状态时,会大大降低系统的吞吐量。
预测I/O调度基于最终期限I/O调度,多了以下操作:
1)新的读请求提交后,并不立即进行请求处理,而是有意等待片刻(默认是6ms)
2)等待期间如果有其他对磁盘相邻位置进行读操作的读请求加入,会立刻处理这些读请求
3)等待期间如果没有其他读请求加入,那么等待时间相当于浪费掉
4)等待时间结束后,继续执行以前剩下的请求
4.4 完全公正的排队I/O调度
每个进程有自己的队列,CFQI/O调度程序把进入的I/O请求放入特定的队列中。调度程序以时间片轮转调度对哦,从每个队列中选取请求数,然后进行下一轮调度。
4.5 空操作的I/O调度
空操作I/O调度只做一件事情,当有新的请求到来时,把它与任一相邻的请求合并。
在启动时通过命令行选项 elevator=xxx 来启用任何一种
参数 | I/O调度程序 |
---|---|
as | 预测 |
cfq | 完全公正排队 |
deadline | 最终期限 |
noop | 空操作 |