《LKD3粗读笔记》(14)块I/O层

news2024/12/23 10:32:57
  • 什么是块设备?
    系统中能够随机访问固定大小数据片的设备被称为块设备,这些数据片称作,最常见的块设备是硬盘
  • 什么是字符设备?
    字符设备按照字符流的方法被有序访问,像串口和键盘就都属于字符设备。
  • 这两种设备的区别是什么?
    区别在于是否可以随机访问,就是能否在访问设备时随意地从一个位置跳转到另一个位置。

1、解刨一个块设备

  • 什么是扇区?
    • 扇区是块设备最小的可寻址单元
    • 扇区的大小是设备的物理属性,扇区大小一般是2的整数倍。
    • 扇区是所有块设备的基本单元,块设备无法对比扇区还小的单元进行寻址和操作。
  • 什么是块?
    • 块是文件系统的一种抽象,只能基于块来访问文件系统
    • 虽然物理磁盘寻址是按照扇区级进行的,但是内核执行的所有磁盘操作都是按照进行的。
    • 对块大小的最终要求是,必须是扇区大小的2的整数倍,并且要小于页面大小

2、缓冲区和缓冲区头(*)

  • 当一个块被调入内存时,它要存储在一个缓冲区。每个缓冲区与一个块对应,前面提到过,块包含一个或多个扇区,但大小不能超过一个页面,所以一个页可以容纳一个或多个内存中的块。由于内核在处理数据时需要一些相关的控制信息,所以每一个缓冲区都有一个对应的描述符,该描述符用buffer_head表示,被称作缓冲区头,在文件linux/buffer_head.h中定义,它包含了内核操作缓冲区所需要的全部信息。
/*
 * Keep related fields in common cachelines.  The most commonly accessed
 * field (b_state) goes at the start so the compiler does not generate
 * indexed addressing for it.
 */
struct buffer_head {
	/* First cache line: */
	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 */
	atomic_t b_count;		/* users using this block */
	u32 b_size;			/* block size */

	sector_t b_blocknr;		/* block number */
	char *b_data;			/* pointer to data block */

	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 */
};
  • 缓冲区头的目的在于描述磁盘块物理内存缓冲区之间的映射关系。这个结构体在内核中只扮演一个角色,说明从缓冲区到块的映射关系。
  • 在2.6内核以前,缓冲区头的作用比现在还重要。因为缓冲区头作为内核的I/O操作单元,不仅仅描述了从磁盘块物理内存的映射,而且还是所有块I/O操作的容器。可是,将缓冲区头作为I/O操作单元带来了两个弊端。
    • 首先,缓冲区头是一个很大且不易控制的数据结构体,而且缓冲区头对数据的操作既不方便,也不清晰。
    • 仅能描述单个缓冲区,当作为所有I/O的容器使用时,缓冲区头会迫使内核打断对大块数据的I/O操作,使其成为对多个buffer_head结构体进行操作。这样做必然会造成不必要的负担和空间浪费。
  • 所以2.5开发版内核的主要目标就是为块I/O操作引入一种新型、灵活并且轻量级的容器,也就是下面要介绍的bio结构体

3、bio结构体

  • 目前内核中块I/O操作的基本容器由bio结构体表示,它定义在文件linux/bio.h中,该结构体代表了正在现场的以片断链表形式组织的I/O操作。一个片段是一小块连续的内存缓存区。这样的话,就不需要保证单个缓冲区一定要连续。所以通过片段来描述缓冲区,即使一个缓冲区分散在内存的多个位置上,bio结构体也能对内核保证I/O操作的执行。
  • 下面给出bio结构体和和各个域的描述。
    /*
     * main unit of I/O for the block layer and lower layers (ie drivers and
     * stacking drivers)
     */
    struct bio {
    	sector_t		bi_sector;
    	struct bio		*bi_next;	/* request queue link */
    	struct block_device	*bi_bdev;
    	unsigned long		bi_flags;	/* status, command, etc */
    	unsigned long		bi_rw;		/* bottom bits READ/WRITE,
    						 * top bits priority
    						 */
    
    	unsigned short		bi_vcnt;	/* how many bio_vec's */
    	unsigned short		bi_idx;		/* current index into bvl_vec */
    
    	/* Number of segments in this BIO after
    	 * physical address coalescing is performed.
    	 */
    	unsigned short		bi_phys_segments;
    
    	/* Number of segments after physical and DMA remapping
    	 * hardware coalescing is performed.
    	 */
    	unsigned short		bi_hw_segments;
    
    	unsigned int		bi_size;	/* residual I/O count */
    
    	/*
    	 * To keep track of the max hw size, we account for the
    	 * sizes of the first and last virtually mergeable segments
    	 * in this bio
    	 */
    	unsigned int		bi_hw_front_size;
    	unsigned int		bi_hw_back_size;
    
    	unsigned int		bi_max_vecs;	/* max bvl_vecs we can hold */
    
    	struct bio_vec		*bi_io_vec;	/* the actual vec list */
    
    	bio_end_io_t		*bi_end_io;
    	atomic_t		bi_cnt;		/* pin count */
    
    	void			*bi_private;
    
    	bio_destructor_t	*bi_destructor;	/* destructor */
    };
    
  • 使用bio结构体的目的主要是代表正在现场执行的I/O操作,所以该结构体中的主要域都是用来管理相关信息的,其中最重要的几个域是bi_io_vecsbi_vcntbi_idx
    在这里插入图片描述
  • bi_io_vecs域指向一个bio_vec结构体数组,该结构体链表包含一个特定I/O操作所需要使用的所有片段。每个bio_vec结构都是一个形式<page,offset,len>的向量,它描述的是一个特定的片段:**片段所在的物理页、块在物理页中的偏移位置、从给定偏移量开始的块长度。**整个bio_io_vec结构体数组表示了一个完整的缓冲区。bio_vec结构体定义在linux/bio.h文件中。
    /*
     * was unsigned short, but we might as well be ready for > 64kB I/O pages
     */
    struct bio_vec {
    	struct page	*bv_page;	/* 指向这个缓冲区所驻留的物理页 */
    	unsigned int	bv_len; /* 这个缓冲区以字节为单位的大小 */
    	unsigned int	bv_offset;	/* 缓冲区所驻留的页中以字节为单位的偏移量 */
    };
    
    
  • 在每个给定的块I/O操作中,bi_vcnt域用来描述bi_io_vec所指向的bio_vec数组中的向量数目,当块I/O操作执行完毕后,bi_idx域指向数组的当前bio_vec片段。bi_cnt域记录bio结构体的使用计数,如果该域值减为0,就应该销毁该bio结构体,并释放它所占用的内存。
  • 缓冲区头和bio结构体的比较
    • bio结构体代表的是I/O操作,它可以包括内存中的一个或多个页
    • buffer_head结构体代表一个缓冲区,它描述的是磁盘中的一个块。因为缓冲区头关联的是单独页中的单独磁盘块,所以它可能会引起不必要的分割,将请求按块为单位划分,只能靠以后才能再重新组合
    • bio结构是轻量级的,它描述的块可以不需要连续存储区,并且不需要分割I/O操作。
    • 并不是说要取消缓冲区头这个概念,毕竟它还负责描述磁盘块到页面的映射。内核通过这两种结构分别保存各自的信息,可以保证每种结构所含的信息量尽可能减少。

4、请求队列

  • 请求队列如何产生?
    块设备将他们挂起的块I/O请求保存在请求队列(request_queue)中,通过内核中像文件系统这样的高层代码将请求插入到队列,只要请求队列不为空,队列对应的块设备驱动程序就会从队列头获取请求并送入对应块设备。
  • 请求队列结构是什么?
    请求队列表中的每一项都是一个单独的请求,由request表示。因为一个请求可能要操作多个连续的磁盘块,所以每个请求可以由多个bio结构组成。(注意,虽然磁盘块必须连续,但内存中这些块不一定要连续,每个bio结构体可以描述多个片段,片段是内存中连续的小区域)

5、I/O调度程序

  • 内核中负责提交I/O请求的子系统称为I/O调度程序。IO调度程序的工作是管理块设备的请求队列
  • I/O调度程序通过两种方法减少磁盘寻址时间:合并排序
  • Linux内核2.6开始引入了全新的IO调度子系统,IO调度器的总体目标是希望让磁头能够总是往一个方向移动,移动到底了再往反方向走,这恰恰就是现实生活中的电梯模型,所以IO调度器也被叫做电梯 (elevator)。而相应的算法也就被叫做电梯算法。而Linux中IO调度的电梯算法有好如下几种:as(Anticipatory)、cfq(Complete Fairness Queueing)、deadlinenoop(No Operation)。具体使用哪种算法我们可以在启动的时候通过内核参数elevator来指定,默认使用的算法是cfq
  1. linus电梯

    • 如果队列中已存在一个对相邻磁盘扇区操作的请求,那么新请求将和这个已经存在的请求合并
    • 如果队列中存在一个驻留时间过长的请求,那么新请求将被插入队列尾部
    • 如果队列中以扇区方向为序存在合适的插入位置,那么新请求插入此位置,保证队列中请求是以被访问磁盘物理位置为序排列
    • 如果队列中不存在合适的请求插入位置,插入到队列尾部。
  2. 空操作的I/O调度程序(NOOP)

    • 空操作的I/O调度程序只会将任一相邻的请求合并。专门为随机访问设备而设计。
    • NOOP算法的全写为No Operation。该算法实现了最最简单的FIFO队列,所有IO请求大致按照先来后到的顺序进行操作。之所以说“大致”,原因是NOOPFIFO的基础上还做了相邻IO请求的合并,并不是完完全全按照先进先出的规则满足IO请求。
      • 假设有如下的io请求序列:
        10050010110561000
        
      • NOOP将会按照如下顺序满足:
        100(101)50010561000
        
      • NOOP是在Linux2.4或更早的版本的使用的唯一调度算法。由于该算法比较简单,减了IO占用CPU中的计算时间最少。不过容易造成IO请求饿死。关于IO饿死的描述如下:因为写请求比读请求更容易。写请求通过文件系统cache,不需要等一次写完成,就可以开始下一次写操作,写请求通过合并,堆积到I/O队列中。读请求需要等到它前面所有的读操作完成,才能进行下一次读操作。在读操作之间有几毫秒时间,而写请求在这之间就到来 ,饿死了后面的读请求 。其适用于SSD或Fusion IO环境下。
  3. 完全公正的排队I/O调度程序(CFQ)

    • 该算法的特点是按照IO请求的地址进行排序,而不是按照先来后到的顺序来进行响应。CFQ为每个进程/线程,单独创建一个队列来管理该进程所产生的请求,也就是说每个进程一个队列,各队列之间的调度使用时间片来调度,以此来保证每个进程都能被很好的分配到I/O带宽。假设有如下的io请求序列:
      • 假设有如下的io请求序列:
        10050010110561000
        
      • CFQ将会按照如下顺序满足:
        10010150010001056
        
      • 在传统的SAS盘上,磁盘寻道花去了绝大多数的IO响应时间。CFQ的出发点是对IO地址进行排序,以尽量少的磁盘旋转次数来满足尽可能多的IO请求。在CFQ算法下,SAS盘的吞吐量大大提高了。但是相比于NOOP的缺点是,先来的IO请求并不一定能被满足,也可能会出现饿死的情况,不过其作为最新的内核版本和发行版中默认的I/O调度器,对于通用的服务器也是最好的选择。CFQ是deadline和as调度器的折中,CFQ对于多媒体应用(video,audio)和桌面系统是最好的选择。
  4. 最终期限I/O调度程序(DEADLINE)

    • DEADLINECFQ的基础上,解决了IO请求饿死的极端情况。除了CFQ本身具有的IO排序队列之外,DEADLINE额外分别为读IO和写IO提供了FIFO队列。读FIFO队列的最大等待时间为500ms,写FIFO队列的最大等待时间为5sFIFO队列内的IO请求优先级要比CFQ队列中的高,而读FIFO队列的优先级又比写FIFO队列的优先级高。优先级可以表示如下:
      FIFO(Read) > FIFO(Write) > CFQ
      
    • Deadline确保了在一个截止时间内服务请求,这个截止时间是可调整的,而默认读期限短于写期限。这样就防止了写操作因为不能被读取而饿死的现象。Deadline对数据库环境(ORACLE RAC,MYSQL等)是最好的选择。
      在这里插入图片描述
  5. 预测I/O调度程序(ANTICIPATORY)

    • CFQDEADLINE考虑的焦点在于满足零散IO请求上。对于连续的IO请求,比如顺序读,并没有做优化。为了满足随机IO和顺序IO混合的场景,Linux还支持ANTICIPATORY调度算法。ANTICIPATORYDEADLINE的基础上,为每个读IO都设置了6ms的等待时间窗口。如果在这6msOS收到了相邻位置的读IO请求,就可以立即满足
    • 本质上与Deadline一样,但在最后一次读操作后,要等待6ms,才能继续进行对其它I/O请求进行调度。可以从应用程序中预订一个新的读请求,改进读操作的执行,但以一些写操作为代价。它会在每个6ms中插入新的I/O操作,而会将一些小写入流合并成一个大写入流,用写入延时换取最大的写入吞吐量。AS适合于写入较多的环境,比如文件服务器,但对对数据库环境表现很差。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/555878.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

从零开始搭建 Lsky Pro 兰空图床

本文源码&#xff1a;https://github.com/chen2438/chenhaotian.top/tree/main/source/_posts/linux-app/lsky.md 在我的博客上查看&#xff1a;https://chenhaotian.top/2022/12/30/linux-app/lsky/ 从零开始搭建 Lsky Pro 兰空图床 官方教程 安装环境 使用OneinStack安装…

第五章 面向对象-8.enum 枚举

enum 枚举 枚举的实例对象是固定的&#xff0c;实例是自动new&#xff0c;每个枚举类会自动继承java.lang.Enum 抽象类 如何声明枚举&#xff1f; 所有枚举类都是java.lang.Enum的子类&#xff0c;无需用extends来继承&#xff0c;如下是枚举类拥有的常用方法 方法名称说明…

测试报告框架 —— Allure2测试报告

目录 Allure2测试报告 1、使用 Allure2 运行方式-Python 2、使用 Allure2 运行方式-Java 3、生成测试报告 4、Allure2 报告中添加用例标题 5、allure2报告中添加用例步骤 6、allure2报告中添加用例链接 7、allure2报告中添加用例分类 8、Allure2 报告中添加用例描述 …

【面试题】前端必修-浏览器的渲染原理

大厂面试题分享 面试题库 前后端面试题库 &#xff08;面试必备&#xff09; 推荐&#xff1a;★★★★★ 地址&#xff1a;前端面试题库 web前端面试题库 VS java后端面试题库大全 1.浏览器的渲染原理 #浏览器是如何渲染页面的 1.什么叫渲染 render 当我们输入一个url地址的…

新书上市 | 从大脑认知开始,全方面提高编程能力,助你摆脱“GPT焦虑症”

目录 一、ChatGPT火爆全网二、《程序员超强大脑》三、本书内容四、本书特色五、作译者简介1、费莉安赫尔曼斯&#xff08;Felienne Hermans&#xff09;2、蒋楠 大家好&#xff0c;我是哪吒。 &#x1f3c6;本文收录于&#xff0c;49天精通Java从入门到就业。 全网最细Java零…

程序人生-Hello’s P2P

摘要 本文讨论了与编程和软件开发相关的几个关键概念和过程。首先介绍了链接的概念和作用&#xff0c;它是将代码和数据片段组合成单一文件的过程&#xff0c;使得分离编译成为可能&#xff0c;从而可以更好地管理和修改模块。接下来探讨了进程的概念和作用&#xff0c;进程是正…

图像数据处理

文章目录 1&#xff1a;TFRecords1-1 将MNIST数据集转换成TFRecord格式1-2 读取TFRecord文件中的数据 2&#xff1a;图像数据的预处理2-1 处理图像编码2-2 调整图像大小2-3 剪裁和填充2-4 按比例剪裁2-5 图像翻转2-6 图像亮度调整2-7 图像对比度调整2-8 图像色相调整2-9 图像饱…

chatgpt赋能Python-python_99乘法

Python编程实现——99乘法表的生成 Python编程语言是一种高级程序设计语言&#xff0c;具有简单易学、可移植性强、功能强大等特点&#xff0c;受到广大开发者的喜爱。Python可以被应用于网站开发、数据分析、人工智能、机器学习等多个领域。而在Python编程中&#xff0c;生成…

《程序员面试金典(第6版)》面试题 02.06. 回文链表(双指针(快慢指针),查找链表中间节点,反转链表)

题目描述 编写一个函数&#xff0c;检查输入的链表是否是回文的。 题目传送门~&#xff1a;面试题 02.06. 回文链表 示例 1&#xff1a; 输入&#xff1a; 1->2 输出&#xff1a; false 示例 2&#xff1a; 输入&#xff1a; 1->2->2->1 输出&#xff1a; true 进…

蓝桥杯单片机串口通信学习提升笔记——部分2

今日继续学习提升蓝桥杯国赛能力水平。 有道是&#xff1a;卜心事、灯花无语&#xff0c;百感孤单&#xff0c;鸳被羞展...... 梦方圆&#xff0c;又丛钟、声声惊断。 诗人杨玉衔孤单影只&#xff0c;偏偏又多遭磨难&#xff0c;一路坎坷...... 正如我近日来学习提升串口通信…

数据结构学习分享之链式二叉树(一)

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:数据结构学习分享⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你了解更多数据结构的知识   &#x1f51d;&#x1f51d; 1. 前言 在学习链式二叉树…

【Linux】shell编程—awk编辑器

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、awk编辑器1.工作流程3.常用选项 二、awk的基础用法1.输出文件中的某一列2.根据特定条件筛选数据3.按照分隔符进行切割4.在匹配到特定字符串时执行操作5.BEGIN打…

chatgpt赋能Python-pythonwxpy

Python的wxpy模块&#xff1a;一款强大的微信机器人框架 在当今数字时代&#xff0c;微信已经成为了大家日常生活中不可缺少的应用。wxpy是一款使用Python语言的微信机器人框架&#xff0c;可以帮助用户实现诸如自动回复、消息提醒、定时发送消息等自动化操作。它的易用性、强…

(5)---STM32 的时钟系统

目录 1.时钟基本概念 时钟源常见振荡器 振荡电路 晶体振荡器 RC振荡器 2.G030时钟源 3.时钟树 4.STM32CubeMX时钟树配置 1.时钟基本概念 1&#xff09; 时钟是嵌入式系统的脉搏&#xff0c;处理器内核在时钟驱动下完成指令执行&#xff0c;状态变换等动作&#xff0c; 外设部件…

基于redis客户端缓存机制实现本地缓存

文章目录 前言一、本地缓存和分布式缓存二、redis客户端缓存机制1.客户端缓存实现原理普通模式广播模式重定向模式redirect 2.优势和误区3.客户端缓存机制请求流程 三、项目实战1.引入依赖2.redis连接属性配置3.开启客户端缓存4.使用本地缓存5.测试 总结 前言 采用缓存一直是我…

VMware ESXi 6.0 U3 Final - ESXi 6 系列最终版下载

VMware ESXi 6.0 U3 Final - ESXi 6 系列最终版下载 VMware ESXi 6 Standard 请访问原文链接&#xff1a;https://sysin.org/blog/vmware-esxi-6/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;sysin.org VersionRelease NameRelease …

RLHF中的PPO算法原理及其实现

RLHF中的PPO算法原理及其实现 ChatGPT是基于InstructGPT实现的多轮对话生成式大模型。ChatGPT主要涉及到的技术包括&#xff1a; 指令微调&#xff08;Instruction-tuning&#xff09;&#xff1b;因果推断&#xff08;Causal Language Modeling&#xff09;&#xff1b;人类…

从零开始Vue3+Element Plus后台管理系统(十五)——多语言国际化vue I18n

i18n国际化的内容比较多&#xff0c;写文章的时间也用得比较长&#xff0c;从上周五开始到本周一&#xff0c;断断续续完成了。 虽然实际工作中很多项目都不需要国际化&#xff0c;但是了解国际化的用法还是很有必要的。 i18n Vue I18n 是 Vue.js 的国际化插件。它可以轻松地…

PFC-FLAC3D Coupling Examples

目录 PFC-FLAC3D Coupling Examples Punch Indentation of a Bonded Material Sleeved Triaxial Test of a Bonded Material 命令流 结果 PFC-FLAC3D Coupling Examples Punch Indentation of a Bonded Material 这个例子展示了一个粘合颗粒模型&#xff08;BPM&#xff0…

项目经历该如何写?

大家好&#xff0c;我是帅地。 这不春招来了吗&#xff0c;帮训练营的帅友们修改了很多简&#xff0c;其中问题最多的就是项目经历 专业技能这块了&#xff0c;特别是项目经历这块&#xff0c;很多人写了一大堆描述功能描述&#xff0c;但是自己具体干了什么却没怎么写&#…