我有一个关于 Linux 内核处理文件 I/O 的一般性问题。目前我的理解是,在理想情况下,进程A读取文件后,数据被加载到页面缓存中,如果进程B在回收之前读取了相同的页面,则不需要再次访问磁盘。
我的问题与块设备 I/O 的工作方式有关。进程 A 的读取请求最终将在 I/O 实际发生之前排队。现在如果要将设备 B 的请求(一个bio
结构体)插入到 中request_queue
,在执行 A 的请求之前,电梯会考虑是否将 B 的请求合并bio
到任何现有的request
中。现在,如果 A 和 B 尝试读取相同的文件偏移量,即相同的设备块,它们实际上是相同的 I/O,(或者 A 和 B 的请求不完全相同,但它们对于某些块重叠),但到目前为止我还没有在内核代码中看到这种情况。(我看到的唯一相关的事情是测试是否bio
可以粘到现有的request
连续。)
内核 2.6.11
inline int elv_try_merge(struct request *__rq, struct bio *bio)
{
int ret = ELEVATOR_NO_MERGE;
/*
* we can merge and sequence is ok, check if it's possible
*/
if (elv_rq_merge_ok(__rq, bio)) {
if (__rq->sector + __rq->nr_sectors == bio->bi_sector)
ret = ELEVATOR_BACK_MERGE;
else if (__rq->sector - bio_sectors(bio) == bio->bi_sector)
ret = ELEVATOR_FRONT_MERGE;
}
return ret;
}
内核 5.3.5
enum elv_merge elv_merge(struct request_queue *q, struct request **req,
struct bio *bio)
{
struct elevator_queue *e = q->elevator;
struct request *__rq;
...
/*
* See if our hash lookup can find a potential backmerge.
*/
__rq = elv_rqhash_find(q, bio->bi_iter.bi_sector);
...
}
struct request *elv_rqhash_find(struct request_queue *q, sector_t offset)
{
struct elevator_queue *e = q->elevator;
struct hlist_node *next;
struct request *rq;
hash_for_each_possible_safe(e->hash, rq, next, hash, offset) {
...
if (rq_hash_key(rq) == offset)
return rq;
}
return NULL;
}
#define rq_hash_key(rq) (blk_rq_pos(rq) + blk_rq_sectors(rq))
这是否意味着内核只会执行两个 I/O?或者(很可能)我错过了什么?
谢谢!