当一个块被调入内存时(也就是说,在读入后或等待写出时),它要存储在缓冲区中。每个缓冲区与一个块对应,它相当于是磁盘块在内存中的表示。块包含一个或多个扇区,但大小不能超过一页,所以一页可以容纳一个或多个内存块。由于内核在处理数据时需要一些相关的控制信息(比如块属于哪个设备,块对应于哪个缓冲区),所以,每一个缓冲区都有一个对应的描述符。该描述符用 buffer_head 结构体表示,称作缓冲区头,在文件  <linux/buffer_head.h> 中定义,它包含了内核操作缓冲区的全部信息。

      下面给出缓冲区头结构体和其中每一个域的说明:

 

struct buffer_head {
         unsigned long b_state;          /* buffer state bitmap (see above) */
         struct buffer_head *b_this_page;/* circular list of page's buffers */
         struct page *b_page;            /* the page this bh is mapped to */
 
         sector_t b_blocknr;             /* start block number */
         size_t b_size;                  /* size of mapping */
         char *b_data;                   /* pointer to data within the page */
 
         struct block_device *b_bdev;
         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 with another mapping */
         struct address_space *b_assoc_map;      /* mapping this buffer is
                                                    associated with */
         atomic_t b_count;               /* users using this buffer_head */
 };

      缓冲区头的目的在于描述磁盘块和物理内存缓冲区之间的映射关系。这个结构体在内核中只是扮演一个描述符的角色,说明从缓冲区到块的映射关系。

      在2.6内核以前,缓冲区头的作用比现在还要重要。因为缓冲区头作为内核中的 IO 操作单元,不仅仅描述了从磁盘块到物理内存的映射,而且还是所有块 IO 操作的容器。但是在2.6内核以后改变了这种策略,它使用一个新的结构 -- bio 来作为操作容器。

      bio 结构体定义于 <linux/bio.h> 中,下面给出 bio 结构体和每个域的描述:

 

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 after coalescing */
        unsigned short       bi_hw_segments;    /* number of segments after remapping */
        unsigned int         bi_size;           /* I/O count */
        unsigned int         bi_hw_front_size;  /* size of the first mergeable segment */
        unsigned int         bi_hw_back_size;   /* size of the last mergeable segment */
        unsigned int         bi_max_vecs;       /* maximum bio_vecs possible */
        struct bio_vec       *bi_io_vec;        /* bio_vec list */
        bio_end_io_t         *bi_end_io;        /* I/O completion method */
        atomic_t             bi_cnt;            /* usage counter */
        void                 *bi_private;       /* owner-private method */
        bio_destructor_t     *bi_destructor;    /* destructor method */
};

      使用 bio 结构体的目的主要是代表正在现场执行的 IO 操作,所以该结构体中的主要域都是用来管理相关信息的,其中最重要的几个域是 bi_io_vecs , bi_vcnt 和 bi_idx 。下图显示了 bio 结构体及其他结构体之间的关系。


      说明:每一个块 IO 请求都通过一个 bio 结构体表示。每个请求包含一个或多个块,这些块存储在 bio_vec 结构体数组中。这些结构体描述了每个片段在物理页中的实际位置,并且像向量一样被组织在一起。 IO 操作的第一个片段由 b_io_vec 结构体所指向,其他的片段在其后依次放置,共有 bi_vcnt个片段。当块 IO 层开始执行请求,需要使用各个片段时, bi_idx 域会不断更新,从而指向当前片段。

      新老方法的比较:

      缓冲区头和新的 bio 结构体之间存在显著差别。bio 结构体代表的是 IO操作,它可以包括内存中的一个或多个页;而另一方面,buffer_head 结构体代表的是一个缓冲区,它描述的仅仅是磁盘中的一个块,所以他可能会引起不必要的分割,将请求按块为单位划分,只能靠以后再重新组合。由于 bio 结构体是轻量级的,它描述的块可以不需要连续存储区,并且不需要分割 IO 操作。

Linux2.6内核--对块IO层操作的讨论的更多相关文章

  1. Linux3.10.0块IO子系统流程(7)-- 请求处理完成

    和提交请求相反,完成请求的过程是从低层驱动开始的.请求处理完成分为两个部分:上半部和下半部.开始时,请求处理完成总是处在中断上下文,在这里的主要任务是将已完成的请求放到某个队列中,然后引发软终端让中断 ...

  2. Linux3.10.0块IO子系统流程(0)-- 块IO子系统概述

    前言:这个系列主要是记录自己学习Linux块IO子系统的过程,其中代码分析皆基于Linux3.10.0版本,如有描述错误或不妥之处,敬请指出! 参考书籍:存储技术原理分析--基于Linux 2.6内核 ...

  3. Linux2.6 内核的 Initrd 机制解析(转)

    from: https://www.ibm.com/developerworks/cn/linux/l-k26initrd/ 简介: Linux 的 initrd 技术是一个非常普遍使用的机制,lin ...

  4. Windows内核原理-同步IO与异步IO

    目录 Windows内核原理-同步IO与异步IO 背景 目的 I/O 同步I/O 异步I/O I/O完成通知 总结 参考文档 Windows内核原理-同步IO与异步IO 背景 在前段时间检查异常连接导 ...

  5. 聊聊Netty那些事儿之从内核角度看IO模型

    从今天开始我们来聊聊Netty的那些事儿,我们都知道Netty是一个高性能异步事件驱动的网络框架. 它的设计异常优雅简洁,扩展性高,稳定性强.拥有非常详细完整的用户文档. 同时内置了很多非常有用的模块 ...

  6. Linux2.6内核实现的是NPTL

    NPTL是一个1×1的线程模型,即一个线程对于一个操作系统的调度进程,优点是非常简单.而其他一些操作系统比如Solaris则是MxN的,M对应创建的线程数,N对应操作系统可以运行的实体.(N<M ...

  7. Hadoop基础-通过IO流操作HDFS

    Hadoop基础-通过IO流操作HDFS 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.上传文件 /* @author :yinzhengjie Blog:http://www ...

  8. (笔记)Linux内核中内存相关的操作函数

    linux内核中内存相关的操作函数 1.kmalloc()/kfree() static __always_inline void *kmalloc(size_t size, gfp_t flags) ...

  9. Linux2.6 内核的 Initrd 机制解析

    文章来自:www.ibm.com/developerworks/cn/linux/l-k26initrd/ 1.什么是 Initrd initrd 的英文含义是 boot loader initial ...

随机推荐

  1. 计数dp-hdu-4054-Number String

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=4055 题目大意: 给一个只含‘I','D','?'三种字符的字符串,I表示当前数字大于前面的数字,D ...

  2. LVS图解 ---阿里

    LVS在大规模网络环境中的应用   1. SLB总体架构   LVS本身是开源的,我们对它进行了多方面的改进,并且也已开源-https://github.com/alibaba/LVS.     接下 ...

  3. IO流--转载

    第 1 部分 从输出流中读取 http://www.ibm.com/developerworks/cn/java/j-io1/ 自早期基于浏览器的 applet 和简单应用程序以来,Java 平台已有 ...

  4. IDL实现主成分变化(PCA)

    IDL只能通过调用envi的二次接口做图像的变换,但是对于普通的数据没有提供函数.根据主成分变换的原理,用IDL写出来了,这样就不用每次再去用matlab的princomp去做了.主成分变化的基本过程 ...

  5. 使用搬瓦工搭建javaweb环境

        /* 本文是基于搬瓦工vps的centos-6-x86_64的Linux系统搭建. 需准备的工具:1.putty(用于连接Linux系统)  2.WinSCP(搬瓦工官方提供的ftp上传下载工 ...

  6. BestCoder冠军赛 - 1009 Exploration 【Tarjan+并查集缩点】

    [题意] 给一个图,这个图中既有有向边,又有无向边,每条边只能走一次,问图中是否存在环. 最多10^6个点,10^6个无向边,10^6个有向边 [题解] 因为既有有向边又有无向边,所以不能单纯的用ta ...

  7. js求字符长度

    </script> <!DOCTYPE html> <html lang="en"> <head> <meta charset ...

  8. Html5 Canvas Text

    html5 canvas中支持对text文本进行渲染;直接的理解就是把text绘制在画布上,并像图形一样处理它(可以加shadow.gradient.pattern.color fill等等):既然它 ...

  9. js apply

    1.作用 函数的apply方法的作用与call方法类似,也是改变this指向,然后再调用该函数.唯一的区别就是,它接收一个数组作为函数执行时的参数 Fn.apply(obj, [arg1, arg2, ...

  10. underscorejs-reduce学习

    2.3 reduce 2.3.1 语法: _.reduce(list, iteratee, [memo], [context]) 2.3.2 说明: reduce方法把list中元素归结为一个单独的数 ...