Linux Block 层在 Linux 内核设计之初就作为几大子系统存在,当然这也是得益于他的前辈 Unix 等优秀的设计。作为 IO 子系统的中间层,他为上层输出接口,为下层提供数据,像个勤劳的小蜜蜂,本文介绍通用块层中的最具传奇色彩的 bio,他就像是一个原子,是在整个 block 层的最小单位,不可分割。

bio 的组成

作为最小单位以及传输介质,那么具体应该长得如何?他又承载着那些信息?

struct bio {

struct bio *bi_next; /* request queue link */

struct block_device *bi_bdev;

unsigned int bi_flags; /* status, command, etc */

int bi_error;

unsigned long bi_rw; /* 末尾 bit 表示 READ/WRITE,

* 起始 bit 表示优先级

*/

struct bvec_iter bi_iter;

/* 当完成物理地址合并之后剩余的段的数量 */

unsigned int bi_phys_segments;

/*

* 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;

/* 关联 bio 的数量 */

atomic_t __bi_remaining;

bio_end_io_t *bi_end_io;

void *bi_private;

unsigned short bi_vcnt; /* how many bio_vec's */

/*

* Everything starting with bi_max_vecs will be preserved by bio_reset()

*/

unsigned short bi_max_vecs; /* max bvl_vecs we can hold */

/* 当前 bio 的引用计数,当该数据为 0 时才可以 free */

atomic_t __bi_cnt; /* pin count */

struct bio_vec *bi_io_vec; /* the actual vec list */

struct bio_set *bi_pool;

/*

* 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.

* 表示跟在 bio 后面的数据集合

*/

struct bio_vec bi_inline_vecs[0];

};

bio 结构体包含了大量的基础信息,这些都是一个基本单元的属性,他们代表着当前这个 bio 的状态,比如是读还是写或者是一些特殊的操作命令等,在 bio 的尾部携带了一个 bi_inline_vecs 数组

最小数据单位 bio_vec

struct bio_vec {

struct page *bv_page;

unsigned int bv_len;

unsigned int bv_offset;

};

顾名思义,bio_vec 就是一个 bio 的数据容器,专门用来保存 bio 的数据,当然他是这个 bio 大集体的一个最小项,刚刚说了 bio 是通用块层的最小集,而这个 bio_vec 则是组成 bio 数据的最小单位,他包含了一块数据所在的页,这块数据所在的页内偏移以及长度,通过这些信息就可以很清晰的描述数据具体位于什么位置,通过对这些数据的整合,可以将他们添加到 SGL(散列表) 中直接发送给后端硬件设备。

迭代器 bvec_iter

寻遍整个 bio 发现居然没有携带需要下盘的扇区编号以及当前 bio 的大小,这个很尴尬,但确实如此,相当于 bio 作为一辆汽车,他携带了货物但是没告诉他目的地这不是完蛋了吗?不是,真正的目的地保存在这个 bvec_iter 中,作为一个迭代器,自然他的使命就是用来遍历 bvec,也就是 bio 数据区。那么他好比就是这辆 bio 汽车的货物分拣员,自然我的目的地不必贴到车上,直接告诉分拣员也是可以的,因为后面的事情可不是这辆汽车再做,而是分拣员需要逐个卸货的时候用。一起来看看迭代器长什么样?

struct bvec_iter {

sector_t bi_sector; /* device address in 512 byte sectors */

unsigned int bi_size; /* residual I/O count */

unsigned int bi_idx; /* current index into bvl_vec */

unsigned int bi_bvec_done; /* number of bytes completed in current bvec */

};

bio 的逻辑架构图

关于 bi_io_vec 和 bi_inline_vecs 的关系

细心的朋友会发现,一个 bio 会有两个字段用来描述 bio 携带的数据,对于没有深入了解的人来讲会比较困惑,其实也比较容易理解,bio 结构在申请内存的时候会多申请 4 个 bvec 的位置跟随 bio 结构体上,这就是 bi_inline_vecs ,他是用来存放内联数据(不能太多,因为申请了不用就会造成内存浪费),当往该 bio 注入数据时,小块的 bio 会直接使用这个内联的数据区域保存小于 4 个 bvec 的信息,而无需重新调用 kmalloc 申请内存,减少了一次内存申请操作,牺牲一小部分内存达到对小 bio 的加速(内存申请过程很长且相对较慢),然后为了兼容后端的其他接口,会直接将 bi_io_vec 指针直接指向 bi_inline_vecs 所在的位置,目的就是告诉后端,数据存放在 bi_inline_vecs 位置上。如果超过 4 个以上的 bvec 才足够完整描述本次 IO 的全部数据,那么 bi_inline_vecs 字段是被直接忽略的,尽管他内存很早就申请好了,但是并不会被采用,而是需要重新自内存管理单元重新申请足额的内存保存这一次的 IO 数据,这就是前面说的一点点的内存浪费的原因。

这是一个 0 长度数组,这不是一个标准 C 的做法,用 std=c99 肯定是编译不过去的,这是一个 std=gnu99 扩展标准的一个特定用法,主要是在结构体中插入一个可以标识紧跟之后的内存的方法,这种方式在 Linux 内核中很常见,是个很巧妙的办法。 ↩︎

linux bio 描述一段内存,Linux 通用块层 bio 详解相关推荐

  1. Linux下查看CPU型号,内存大小,硬盘空间的命令(详解)

    1 查看CPU 1.1 查看CPU个数*核心数 cat /proc/cpuinfo | grep "physical id" | uniq | wc -l 96 #一共96核 2 ...

  2. linux c free大段内存,Linux C 动态内存分配--malloc,new,free及相关内容

    一.malloc()和free()的基本概念以及基本用法: 1.函数原型及说明: void *malloc(long NumBytes):该函数分配了NumBytes个字节,并返回了指向这块内存的指针 ...

  3. Linux内核中kzalloc分配内存时用的参数GFP_KERNEL详解

    简介 GFP(Get Free Pages缩写)在include/linux/gfp.h中定义. GFP_KERNEL 是内核内存分配时最常用的,无内存可用时可引起休眠. GFP_ATOMIC 用来从 ...

  4. 漫谈linux文件io,Linux文件IO与通用块层的请求合并

    本文参考https://mp.weixin.qq.com/s/Imt4BW-zoHPpcOpcKZs_AQ, 公众号"Linux阅码场" 请求合并就是将进程内或者进程间产生的在物理 ...

  5. linux内存管理机制以及free命令详解

    linux内存管理机制以及free命令详解 一.linux内存管理机制 1.物理内存和虚拟内存 直接从物理内存读写数据要比从硬盘读写数据要快的多,因此,我们希望所有数据的读取和写入都在内存完成,而内存 ...

  6. 19. linux中权限详解,Linux权限位,读写执行权限真正含义,chmod详解

    linux中权限详解,Linux权限位,读写执行权限真正含义,chmod详解 文章目录 Linux权限位 读写执行 三种权限真正含义和作用 权限对文件的作用 权限对目录的作用 示例 chmod 使用数 ...

  7. Linux进程最大socket数,Linux下高并发socket最大连接数所受的各种限制(详解)

    1.修改用户进程可打开文件数限制 在Linux平台上,无论编写客户端程序还是服务端程序,在进行高并发TCP连接处理时,最高的并发数量都要受到系统对用户单一进程同时可打开文件数量的限制(这是因为系统为每 ...

  8. linux系统编程之进程(八):守护进程详解及创建,daemon()使用

    linux系统编程之进程(八):守护进程详解及创建,daemon()使用 一,守护进程概述 Linux Daemon(守护进程)是运行在后台的一种特殊进程.它独立于控制终端并且周期性地执行某种任务或等 ...

  9. linux如何确定共享库路径,摘录Linux下动态共享库加载时的搜索路径详解

    对动态库的实际应用还不太熟悉的读者可能曾经遇到过类似"error while loading shared libraries"这样的错误,这是典型的因为需要的动态库不在动态链接器 ...

最新文章

  1. ES6新特性(函数默认参数,箭头函数)
  2. USRP E310启用SSH的X11 Forwarding功能
  3. [EffectiveC++]item41:了解隐式接口和编译期多态
  4. oracle建表后添加数据报错:ORA-01658:无法为表空间中的段创建INITIAL区
  5. JavaScript通过RegExp实现客户端验证
  6. Css3新属性:calc()
  7. 剥开比原看代码07:比原节点收到“请求区块数据”的信息后如何应答?
  8. 从SQL到NoSQL再到NewSQL
  9. IOS学习之UITableView滚动到指定位置
  10. 华三交换机链路聚合的几点思考
  11. ACT开发初步(二)——XML
  12. Linux学习笔记(详细)
  13. 关于chrome上的网银安全控件开发技术(chrome 调用本地dll)
  14. DeepStream:下一代智慧城市的视频分析
  15. Docker 数据存放位置
  16. IDEA集成docker-maven-plugin配置CA安全证书
  17. JVM菜鸟进阶高手之路十(基础知识开场白)
  18. 友价商城不支持php5.3_PHP友价T5商城源码 UC论坛整合送手机版程序七套模板_源码下载...
  19. 基于matab GUI的图形处理火焰检测系统
  20. Deepin20固定无线网卡多个IP地址

热门文章

  1. 视频清晰度与数据密度
  2. 超入门级-基于中值滤波处理ECG信号的基线漂移-Python-MIT-BIH数据集
  3. python干货:如何使用Python对音频进行特征提取?
  4. 解决Openwrt安装插件提示一下错误的办法
  5. SSH不能传输文件问题
  6. 英文文献翻译的APP
  7. 使用 Vite 插件自动化实现骨架屏
  8. [Spring Boot 6]企业级开发
  9. Linux Debian 系统的单网卡绑定多个IP地址的操作步骤
  10. GUI(图形用户界面)——AWT概述、布局管理器