4

我有一个关于 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?或者(很可能)我错过了什么?

谢谢!

4

0 回答 0