The Block I/O Layer

作者: july2993 | 来源:发表于2018-03-08 15:20 被阅读4次

    http://sylab-srv.cs.fiu.edu/lib/exe/fetch.php?media=paperclub:lkd3ch14.pdf
    这里mark一些要点跟总结等。

    Anatomy of a Block Device

    • 快设备最小寻址单位是sector, 多数是512bytes
    • kernel寻址单位block, 是sector大小的power-of-two倍数,不大于page size, 常见512B, 1KB, 4KB
    • 机械硬盘的扇区这些概念是对特定block设备的属性,kernel在sector上抽象block

    Buffers and Buffer Heads

    • block在内存里变现为一个buffer
    • 每个buffer有一个buffer head 对应这个buffer的信息(哪个device, block等)
    struct buffer_head {
      unsigned long b_state; /* buffer state flags */
      struct buffer_head *b_this_page; /* list of page’s buffers */
      struct page *b_page; /* associated page */
      sector_t b_blocknr; /* starting block number */
      size_t b_size; /* size of mapping */
      char *b_data; /* pointer to data within the page */
      struct block_device *b_bdev; /* associated block device */
      bh_end_io_t *b_end_io; /* I/O completion */
      void *b_private; /* reserved for b_end_io */
      struct list_head b_assoc_buffers; /* associated mappings */
      struct address_space *b_assoc_map; /* associated address space */
      atomic_t b_count; /* use count */
    };
    

    The bio Structure

    struct bio {
        sector_t bi_sector; /* associated sector on disk */
        struct bio *bi_next; /* list of requests */
        struct block_device *bi_bdev; /* associated block device */
        unsigned long bi_flags; /* status and command flags */
        unsigned long bi_rw; /* read or write? */
        unsigned short bi_vcnt; /* number of bio_vecs off */
        unsigned short bi_idx; /* current index in bi_io_vec */
        unsigned short bi_phys_segments; /* number of segments */
        unsigned int bi_size; /* I/O count */
        unsigned int bi_seg_front_size; /* size of first segment */
        unsigned int bi_seg_back_size; /* size of last segment */
        unsigned int bi_max_vecs; /* maximum bio_vecs possible */
        unsigned int bi_comp_cpu; /* completion CPU */
        atomic_t bi_cnt; /* usage counter */
        struct bio_vec *bi_io_vec; /* bio_vec list */
        bio_end_io_t *bi_end_io; /* I/O completion method */
        void *bi_private; /* owner-private method */
        bio_destructor_t *bi_destructor; /* destructor method */
        struct bio_vec bi_inline_vecs[0]; /* inline bio vectors */
    };
    
    Figure 14.2
    • bio 替代buffer_head表示一次io 操作,buffer只跟block对应
    • The basic container for block I/O within the kernel is the bio structure。

    The Old Versus the New

    • bio 可以容易表示high memory, bio处理对应物理page,不是pointer
    • 可以同时表示 normal page I/O 跟direct I/O
    • 容易处理涉及多个物理页的操作
    • 相比buffer head更轻量, 只包含一个block I/O操作需要的信息

    buffer head的概念仍然需要,但只表示block到buffer的对应,bio表示in-flight I/O.

    Request Queues

    • 设备维护一个request queues存储pending的block I/O request, 有定义在<linux/blkdev.h>里的request_queue structure表示,包含一个请求的双向链表跟相关信息。
    • request由高层的代码如文件系统添加。
    • queue非空快设备驱动就从queue里队首获取request提交到对应块设备。
    • 一个request由<linux/blkdev.h>里的struct request表示,可以包含多个bio, 因为一个request可以操作多个连续的disk blocks.

    I/O Schedulers

    如果kernel需要io request的时候就丢到queue的那么性能会很差(考虑磁盘seek操作)。所以kernel会有mergingsorting的操作来提升性能,提供这些操作的子系统就叫做 I/O scheduler

    The Job of an I/O Scheduler

    一个I/O scheduler管理块设备的request queue.它通过决定request被分发到块设备的顺序跟时间来提高整体的吞吐。

    • merging
      两个或多个request合并成一个。比如文件系统提交一个request,但queue里已经有一个request读取相邻的section,可以合并减少overhead&seed
    • sorting
      没有相邻的request不能合并,但有相近的section请求可以调整顺序。比如把读第3个sector的放在读第1个sector的请求后面。(想下电梯调度)

    The Linus Elevator

    Linus Elevator 是第一个I/Oscheduler, 2.4默认,2.6后被其它替代。

    Linus Elevator会执行merging和sorting操作,但添加一个request是,会在queue里检查每个request看有没相邻(前或后相邻)的request合并。

    如果不能merging会找一个sectorwise的合适位置插入,不然插入的队列末尾。另外如果有request在队列里超过了一定时间也会把当前request放到队列而不是插入到合适位置,这个为了比较某个位置的大量请求饿死其它位置的请求。

    总的说当添加一个request有4个操作按序可能:

    • 队列里有相邻的request, 合并.
    • 队列里有相当old的request.没处理了, 把新的插到队尾防止饿死其它更老的requests.
    • 有合适的sector-wise位置在队列里插入到对应位置,让队列保持按磁盘物理顺序排序。
    • 没合适位置,简单插到队尾。

    The Deadline I/O Scheduler

    Deadline I/O Scheduler用来解决Linux Elevator产生的饥饿问题。为了减少seeks的时间,大量同一区域的磁盘操作容易饿死距离较远的request, 这不公平。

    更糟糕的是,上面的request 饥饿问题会产生write starving reads.写request当丢到队列就可以当提交了,对应用异步。读当应用提交request, 应用会block到reqeust处理完拿到数据。这样read lantency对上层应用很重要,虽然希望对于写 lantency也不能太大。

    读请求更加趋向于互相依赖。例如读取大量文件,每个读在一个很小的buffered chunks。应用不会读取下个chunk(活着说下个文件),知道前一个chunk已经读取并返回到应用,更糟糕的是写也要读(文件系统读取元数据如inode)。读取这些block会串行化I/O。因此,每个读请求都饥饿的化对于应用操作的lantency会很大。Deadline I/O scheduler实现若干特性来确保读饥饿最小化。

    要知道减少读饥饿会带来全局吞吐下降的问题。Linux Elevator也是这样,Linux Elevator提供更好的吞吐(通过更大力度的减小 seeks)。Deadline I/O scheduler通过努力限制饥饿同时提供好的全局吞吐。

    在Deadline I/O scheduler每个request会有一个超时时间。默认读500ms, 写5s。如图Deadline scheduler会维护一个类似the Linux Elevator的队列,这个队列按磁盘物理排序,同样会执行mergingsorting的操作。同时根据读或者写的类型插入对另一对应队列(FIFO, 即按时间排序)。Deadline scheduler从sorted queue垃取request到dispatch queue给磁盘驱动消费,这个最小化seeks次数时间。

    当在read queue或write queue的request超时了的时候,Deadline scheduler 就从这些FIFO的队列拿取request而不是sorted queue。通过如此,Deadline scheduler尝试确保没有请求远大于它的超时时间。

    所以不确保在超时时间内处理,但通过给读请求一个相当小的超时时间这可以防止write starve read, 读会提供更好的lantency。

    The Anticipatory I/O Scheduler

    The Complete Fair Queuing I/O Scheduler

    CFQ类似the Linux Elevator,但是每个进程维护一个queue, 分别merge跟sort, round robin处理每个进程(默认每次获取4个request)。
    为多媒体workload设计,但对于多数情景也很好。
    (应该是2.6的默认scheduler)

    The Noop I/O Scheduler

    会做merging但不sorted,基本没什么操作,对于random-access的设备比较好。

    Conclusion

    • bio 表示 in-flight I/O
    • buffer_head 表示一个block-to-page mapping
    • request structure 表示一个特定的I/O请求。
      request最后到scheduler处理调度,由driver处理。

    一般来说 NOOP 调度器最适合于固态硬盘,DeadLine 调度器适用于写入较多的文件服务器,比如Web服务器,数据库应用等,而CFQ 调度器适合于桌面多任务及媒体应用。


    看最新linux(4.12)代码下有的I/O scheduler

    ➜  linux git:(master) ✗ ll block/*iosched.c
    -rw-r--r--  1 huangjiahao  staff   162K May 23  2017 block/bfq-iosched.c
    -rw-r--r--  1 huangjiahao  staff   127K May 23  2017 block/cfq-iosched.c
    -rw-r--r--  1 huangjiahao  staff    11K May 23  2017 block/deadline-iosched.c
    -rw-r--r--  1 huangjiahao  staff    21K May 23  2017 block/kyber-iosched.c
    -rw-r--r--  1 huangjiahao  staff   2.6K May 23  2017 block/noop-iosched.c
    

    bfq跟kyber应该是4.12才加入的,参见:https://lwn.net/Articles/720675/

    看完可能想看下当前硬盘上用什么调度器了

    ➜  ~ cat /sys/block/sda/queue/scheduler
    noop deadline [cfq]
    

    括号起来的cfq就是当前用的,当前有3种可以配置使用。

    相关文章

      网友评论

        本文标题:The Block I/O Layer

        本文链接:https://www.haomeiwen.com/subject/fgwsfftx.html