block bio queue request等

1. queue_flag_set_unlocked(QUEUE_FLAG_NOMERGES, ns->queue);

不允许对队列的request进行merge操作

2. blk_queue_virt_boundary(ns->queue, dev->page_size - 1);

to ensure there are no ‘holes‘ in the presented
sg list (all segments in the middle of the list need to be of PAGE_SIZE).Setting virt_boundary_mask to PAGE_SIZE - 1 guarantees we‘ll never see
such holes

The block layer can reliably guarantee that SG lists won‘t
contain gaps (page unaligned) if a driver set the queue
virt_boundary.

With this setting the block layer will:
- refuse merges if bios are not aligned to the virtual boundary
- split bios/requests that are not aligned to the virtual boundary
- or, bounce buffer SG_IOs that are not aligned to the virtual boundary

理解是block层保证request bio中的数据是page对齐的,不对齐的话会分成多个request

对磁盘的抽象genhd.c和对分区的抽象:partition-generic.c和partitions目录下的文件

l  上层文件系统会把对文件访问转变为对多个sector的访问,这些sector很可能在内存中是分离的。所以需要一种数据表示方法,用来表示要读写的数据内容。这这个数据结构叫做bio(很奇怪的是这里为什么不直接使用scsi使用的scatterlist?)

l  scsi相关

n  新的scsi标准有DIF/DIX的数据保护机制,无论对于读还是写的数据,都需要一个数据完整性的校验,由于在通用块层存储数据的结构体是bio,所以对其进行校验的文件叫做bio-integraty.c。这个文件完成的是与内存相关的设置,真正的算法在blk-integraty.c中定义的一系列钩子函数。不同的硬件会注册不同的计算方法供本层调用。也就是说,这里实际实现的是DIX协议。

n  本层要知道scsi的接口,本层定义了bsg(blockSCSI generic device)的v4接口,在bsg.c和bsg-lib.c

n  t10保护的支持算法t0-pi.c,scsi的ioctl:scsi_ioctl.c

l  连接本层各个功能组件的核心程序:blk-core.c,还包括一些辅助文件实现些特定的周边。这一部分包括

n  内核执行这部分代码不是阻塞的,而是使用内核线程完成的,使用的是kblockd,其定义和相关功能位于blk-core.c中

n  request的处理(bio只是数据的存储结构,但是一个命令请求不只有数据,还需要有其他控制和状态信息,这些信息和bio一起被组织到request中),但是要注意的是request和bio都只是本层的数据结构,request服务于电梯算法,bio用于盛放用户传进内核的数据

u  将用户数据映射到bio结构体的blk-map.c

u  将request中的bio数据映射到下层(scsi)使用的scatterlist结构体的处理程序blk-merge.c

u  request如果超过了一定的时间需要被time out掉,代码在blk-timeout.c

u  request请求到的数据在本层需要有缓冲,可以从中提取提交到上层上层所需要的数据,而丢弃或者缓存一部分上层没有要到的数据。这种行为叫做bounce,功能定义在bounce.c中

n  队列(queue)处理(对于块设备的一系列命令,需要队列缓存,并且这一层最重要的,队列中的各个命令有可能可以合并为一个,例如读取连续的数据的两个命令,由于每次存取数据的量越大,越节省时间,所以这一步是提高效率的关键)

u  linux的设计者将对queue的插入执行操作单独的提取出来放到blk-exe.c中

u  对队列的属性进行设置的blk-settings.c

u  对队列中的request添加ID(tag),可以通过该tag直接找到该request,实现在blk-tag.c

u  凡是通信管道都要考虑流量控制问题。queue可以有多个来源,如果某个来源瞬间提交了过多的bio,那么其他来源的bio就可能饥饿。防止这种现象发生需要给队列针对某一个来源添加一个阈值,这个阈值的控制在blk-throttle.c

n  电梯算法接口。上一条说的合并多个request的操作,需要有合并的算法,合并的算法有很多,但是核心部分要为这些算法提供调用的接口函数

n  提交请求。当电梯算法被执行完,多个request和其对应的bio被合并,这个bio就需要被提交到下层(scsi的上层)去实际的执行发送。发送完毕还要执行回调。这部分代码也在这里提供。

l  电梯算法:电梯算法在queue上执行合并操作,是性能优化的关键。代码位于elevator.c,deadline-iosched.c,cfq-iosched.c,noop-iosched.c,还有提供优先级的ioprio.c

l  对于IO上下文的处理。IO上下文是在request上层的数据结构,如果说通用块层处理的request级别的数据结构,文件系统就是处理的IO上下文。而文件系统层次包括同步和异步两种数据模式,这里的IO上下文(io_context)主要是用在异步,异步IO在提交IO请求前必须要初始化一个IO上下文,一个IO上下文会包含多个request。通用块层对IO上下文的处理函数放在blk-ioc.c。

l  正常的逻辑是发送了IO命令,命令请求完毕后会调用回调函数。但是,通用块层允许poll操作,就是没有回调函数,请求执行完后需要用户手动查询和处理。这部分代码在blk-iopoll.c

l  对本层命令队列的处理可以有一个CPU,也可以有多个。如果多个,就需要对队列进行特殊的优化,叫mq,相关代码位于blk-mq.c、blk-mq-cpu.c、blk-mq-cpumap.c、blk-mq.h、blk-mq-sysfs.c、blk-mq-tag.c、blk-mq-tag.h中

l  内核处理命令的返回结果,在通用块层不可能是使用硬中断,所以这里的回调使用的是软中断,定义在blk-softirq.c

l  实现sysfs接口,定义在blk-sysfs.c,实现cgroup子系统的blk-cgroup.c

l  其他的辅助功能组件:将内容flush进磁盘的blk-flush.c、辅助函数blk-lib.c、用来解析磁盘信息返回值的cmdline-parser.c、提供ioctl接口的compat_ioctl.c,ioctl.c

l

从以上可以看出,这一部分的关键组件是:request、queue、bio、elevator和磁盘与分区的抽象。

数据完整性校验

如果要对bio进行数据完整性校验,需要调用bio_integraity_alloc给bio分配对应的空间,之后通过bio_integraty_add_page给bio添加额外的空间,用bio_free就会自动删除掉分配的空间。

具体的计算bip(dif)的算法由具体的驱动提供,驱动调用的是blk_integraty_register来注册自己的计算函数。

在文件系统中,可以通过/sys/block/<bdev>/integraty/目录下的write_generate和read_verify来控制是否执行读写校验。

大部分情况下,数据完整性对于文件系统是透明的,但上层的文件系统仍可以显示的使用DIX。在bio_integraty_enabled为1的情况下,上层调用bio_integrity_prep为bio准备bip。

磁盘设备在注册是可以生成blk_integrity结构体,体面就是存放具体的读写校验函数和tag的大小。

设备抽象

linux的通用快层对磁盘的抽象是gendisk结构体,该层以下的各种设备都是这个结构体的一种。例如scsi磁盘设备scsi_disk就是gendisk的一种。

对于分区的抽象是structpartition。

设备驱动抽象
设备驱动操作抽象

block_device_operations

对设备进行驱动

设备操作指令抽象

对设备进行指令操作的结构体是struct request,连接通用快层和下层设备指令操作的数据结构是bio,bio在request中,被上层识别,也被下层识别。

磁盘检测

这部分描述当发现插入了磁盘或者是删除了磁盘时,内核是如何反应的。

BIO和bio_set

BIO是通用块层表达数据的方式,其将用户传递进来的数据转换为bio存储,bio又包含进了request。多个bio可以组成链接,bio中内生提供链表结构。


struct bio {

struct bio          *bi_next;        /*BIO 链表*/

struct block_device *bi_bdev;    //文件系统层的块设备抽象

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

unsigned long           bi_rw;                /* 标示是读还是写的标志位 */

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;

atomic_t           bi_remaining;

bio_end_io_t             *bi_end_io;    //BIO全部执行结束的回调函数

void                    *bi_private;

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

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

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.

*/

struct bio_vec           bi_inline_vecs[0];

};

内核里一个bio有多个bio_vec,一个bio_vec叫一个segment。由于上层提交来的bio中的bio_vec,所以bio本身也是可以合并的。但是每个queue可以有标志位QUEUE_FLAG_NO_SG_MERGE控制是否允许bio的合并。如此,bio就有了两种统计口径:bi_vcnt表示bio没有经过自身合并的bio_vec数目,bi_phys_segments表示将物理连续的bio_vec算成一个后统计出来的段总数。这里需要注意的是:bio的段总数并不是单个bio的段的数目,而因为bio天生是个链表,所以段的数目总是统计的是链表中段的总数。

BIO标志

BIO_SEG_VALID:bi_phys_segments有了有效值后置这个标志位。

BIO操作

bio_flagged(bio,flag)用于检测bio的bi_flags域是否与flag相等。

request

request中包含了bio和其他参数,例如表明携带数据总大小的__data_len。用双下划线的域一般是不直接使用,而是要使用辅助函数调用,典型的是blk_rq_bytes(const struct request *rq)函数返回这个值,而

static inline unsigned intblk_rq_sectors(const struct request *rq)

{

returnblk_rq_bytes(rq) >> 9;

}

又可以返回这个request携带的sector的数目。

struct request {

struct list_head queuelist;

union {

struct call_single_data csd;

unsigned long fifo_time;

};

...}

结构体的定义都是和功能相关的。由于bio可以被合并进一个request,所以request要为这种功能提供支持。bio合并进request可以在原bio的前面合并也可能在后面。如果在前面,那么肯定是在最前面,此时直接利用bio本身的链表结构插入到最前面即可。如果在后面,也肯定是在最后面,但是此时没有使用bio本身的链表结构,而是使用了一个额外的域,叫biotail来盛放要合并进入的bio。因为这个域本身的定义就是用来放最后一个bio。向前合并最后一个bio不变,而向后合并最后一个bio要变化。

request中的域分为3类,分别用在3个不同的地方:驱动、通用块层、IO调度。

request标志

REQ_FLUSH:表示执行bio前进行fluash。REQ_FUA表示执行bio后进行flush。

QUEUE_FLAG_NO_SG_MERGE:表示是否允许bio本身的bio_vec进行物理合并。

request_queue

这是通用块层的请求队列,这个队列一个cpu一个。上层的数据请求首先生成bio,然后由bio生成request,然后添加到request_queue,然后request_queue会被执行。这个执行包括很多步骤,最重要的是电梯算法。每个算法都会在全局的request_queue之外生成自己的队列结构体elevator_queue。

Queue属性

Queue的标志

#define QUEUE_FLAG_QUEUED      1       /*uses generic tag queueing */

#define QUEUE_FLAG_STOPPED     2       /*queue is stopped */

#define     QUEUE_FLAG_SYNCFULL         3       /*read queue has been filled */

#define QUEUE_FLAG_ASYNCFULL 4       /*write queue has been filled */

#define QUEUE_FLAG_DYING 5       /*queue being torn down */

#define QUEUE_FLAG_BYPASS         6       /*act as dumb FIFO queue */

#define QUEUE_FLAG_BIDI              7       /*queue supports bidi requests */

QUEUE_FLAG_NOMERGES:直接不允许对队列的request进行merge操作

#define QUEUE_FLAG_SAME_COMP      9       /*complete on same CPU-group */

#define QUEUE_FLAG_FAIL_IO     10 /*fake timeout */

#define QUEUE_FLAG_STACKABLE   11        /*supports request stacking */

#define QUEUE_FLAG_NONROT      12     /*non-rotational device (SSD) */

#define QUEUE_FLAG_VIRT        QUEUE_FLAG_NONROT /* paravirt device */

#define QUEUE_FLAG_IO_STAT     13         /*do IO stats */

#define QUEUE_FLAG_DISCARD     14       /*supports DISCARD */

#define QUEUE_FLAG_NOXMERGES   15     /*No extended merges */

#define QUEUE_FLAG_ADD_RANDOM  16  /*Contributes to random pool */

#define QUEUE_FLAG_SECDISCARD  17       /*supports SECDISCARD */

#define QUEUE_FLAG_SAME_FORCE  18      /*force complete on same CPU */

#define QUEUE_FLAG_DEAD        19       /*queue tear-down finished */

#define QUEUE_FLAG_INIT_DONE   20        /*queue is initialized */

#define QUEUE_FLAG_NO_SG_MERGE 21     /* don‘t attempt to merge SG segments*/

#define QUEUE_FLAG_SG_GAPS     22       /*queue doesn‘t support SG gaps */

#define QUEUE_FLAG_DEFAULT      ((1 << QUEUE_FLAG_IO_STAT) |               \

(1 << QUEUE_FLAG_STACKABLE)  |       \

(1 << QUEUE_FLAG_SAME_COMP)       |       \

(1 << QUEUE_FLAG_ADD_RANDOM))

#define QUEUE_FLAG_MQ_DEFAULT      ((1 << QUEUE_FLAG_IO_STAT) |               \

(1 << QUEUE_FLAG_SAME_COMP))

queue的极限

时间: 2024-08-17 11:47:02

block bio queue request等的相关文章

NSConnection_异步下载Block

NSString * path = @"http://10.0.8.8/sns/my/user_list.php"; NSURL * url = [NSURL URLWithString:path]; NSURLRequest * request = [NSURLRequest requestWithURL:url]; //发送异步请求 //Queue 队列 线程 //[NSOperationQueue mainQueue] 主线程 //handler 程序处理 请求完成之后的处理bl

Linux Block I/O 简介

1.where is block I/O layer in the system 2.简介 The generic block I/O layer is an abstraction for blcok devices in the system Receives I/O requests in a queue, and is responsible for passing them along to block devices. The I/O requests are scheduled t

python开发线程:死锁和递归锁&amp;信号量&amp;定时器&amp;线程queue&amp;事件evevt

一 死锁现象与递归锁 进程也有死锁与递归锁,在进程那里忘记说了,放到这里一切说了额 所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去.此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程,如下就是死锁 from threading import Thread,Lock import time mutexA=Lock() mutexB=Lock() class MyThread(Thread):

13 join 线程锁之Lock\Rlock\信号量 将线程变为守护进程 Event事件  queue队列 生产者消费者模型 Queue队列 开发一个线程池

本节内容 操作系统发展史介绍 进程.与线程区别 python GIL全局解释器锁 线程 语法 join 线程锁之Lock\Rlock\信号量 将线程变为守护进程 Event事件 queue队列 生产者消费者模型 Queue队列 开发一个线程池 进程 语法 进程间通讯 进程池 操作系统发展史 手工操作(无操作系统) 1946年第一台计算机诞生--20世纪50年代中期,还未出现操作系统,计算机工作采用手工操作方式. 手工操作程序员将对应于程序和数据的已穿孔的纸带(或卡片)装入输入机,然后启动输入机把

详细解读Volley(二)—— ImageRequest &amp; Request简介

上篇文章我们讲到了如何用volley进行简单的网络请求,我们可以很容易的接受到string.JsonObjec类型的返回结果,之前的例子仅仅是一次请求,这里需要说明volley本身就是适合高并发的,所以它可以运行你用volley在短时间内进行多次请求,并且不用去手动管理线程数.仅仅是请求文字过于基础了,本篇将讲述如何用volley从网络下载图片. 一.用ImageRequest来请求图片 ImageRequest是一个图片请求对象,它继承自Request<Bitmap>,所以请求得到的结果是一

进击的Python【第九章】:paramiko模块、线程与进程、各种线程锁、queue队列、生产者消费者模型

一.paramiko模块 他是什么东西? paramiko模块是用python语言写的一个模块,遵循SSH2协议,支持以加密和认证的方式,进行远程服务器的连接. 先来个实例: 1 import paramiko 2 # 创建SSH对象 3 ssh = paramiko.SSHClient() 4 5 # 允许连接不在know_hosts文件中的主机 6 ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 7 # 连接服务器 8 ss

深入了解Volley如何执行一个Request的流程

Volley主要类构成 Volley: 框架同名类,负责创建RequestQueue对象 Request:代表一个网络请求的抽象类,具体实现子类有(StringRequest,JsonRequest,ImageRequest),需添加到RequestQueue中操作.包含了url,请求方法,请求Header,请求Body,请求的优先等级信息. RequestQueue:核心类,负责维护所有的Request对象,完成请求操作.该类中有两个基于优先级的Request队列mCacheQueue,mNe

iOS开发之Block实现同异步加载

一. 相关知识 1.控制层 负责数据的交互.控制层请求数据,会创建一个具有网络请求的对象.对象中有一个block,block会将请求到的数据回调给控制层. 2.数据的传输过程 Request(请求到数据后) 回调给Controller 二. 数据同步加载 思想: Request对象负责下载网络数据.Controller中需要下载的数据,但直接在Controller中实现下载功能,不是一种好的习惯.采取在Request中实现下载功能,同时设置block属性,将下载数据回调给Controller(前

容器(Queue/Deque/Enumeration/Hashtable/Properties等)

一.队列(Queue/Deque) Queue:单向 队列通常FIFO(先进先出).优先级队列和堆栈队列(后进先出) add 增加一个元索 如果队列已满,则抛出一个IIIegaISlabEepeplian异常 remove 移除并返回队列头部的元素 如果队列为空,则抛出一个NoSuchElementException异常 element 返回队列头部的元素 如果队列为空,则抛出一个NoSuchElementException异常 offer 添加一个元素并返回true 如果队列已满,则返回fal