[048]Block Layer层架构演变(个人YY)
前言
Block Layer层在整个I/O中负责承上启下,上接文件系统,下接块驱动。
我不想直接讨论代码,希望从一个架构的演变来初探一下Block Layer层。
一、1.0版本
首先我们来了解几个重要的数据结构
1.1 bio
bio代表了一次I/0请求,代表一个块设备的一个扇区或者多个连续扇区的数据请求,扇区是块设备的最小访问单元,bio是文件系统发给Block Layer层的。
1.2 request
request代表块设备可以处理的一次任务单元,一个request由一个bio或者多个扇区相连的bio组成。
1.3 架构图
1.4 小结
Block Layer将文件系统提供的bio变成request,然后交给块设备驱动处理。
二、2.0版本
目前只支持单个进程访问块设备。为了可以支持多进程,我们在块设备驱动中创建一个队列:request_queue,所有的进程发起bio生成的request,会被投递到request_queue,块设备循环处理request_queue中的request。
2.1 request_queue
每一个块设备都有一个工作队列,request_queue,保存准备就绪待处理的request。
2.2 架构图
2.3 小结
这样子就实现了多个进程同时访问块设备,而且在新的一个bio变成request之前,可以先看看该bio能否合并到request_queue的某个request,这样子就可以提升bio处理的效率。
三、3.0版本
好像看起来2.0已经挺完美了,但是request_queue会有一把锁控制,如果多个进程短时间发起多个bio,多个进程不断竞争锁,这样子性能不是很好,我们就引入了plug。
3.1 plug
plug翻译成中文就是塞子,每一个进程会有一个plug,plug是一个request的list,先将一个进程短时间发起的所有bio生成request先堆积到当前进程的plug list,然后在等待特定时机释放plug中的requests到request_queue,这样子可以提升bio处理的效率。
3.2 架构图
3.3 小结
架构越来越完美了,除了上面的流程,在新的一个bio变成request之前,先看看能不能合并到plug里面的某个request中,如果不行在,再看看能否合并到request_queue里面的某个request中,如果不行再变成request,然后放到plug list中,等待积攒一定量之后,统一放置request_queue里面。
四、4.0版本
看起来好像3.0已经很完美了,但是我们会发现3.0有两个明显的优化点。
优化点1:
传统的块设备是机械磁盘,这种硬件是靠一个磁头不断移动进行访问数据的,如果我们可以把所有request排序,这样子就可以提升块设备的访问效率。
举个例子:一个外卖员需要送很多外卖到一个办公楼,他将外卖按楼层排序合并,然后一层层的送,肯定比随机拿一个外卖,然后一层层的送的速度快。
优化点2:
每个块设备单位时间处理request的次数是有限的,如果我们可以提升前台的进程request被处理的优先级,这样子就可以带来更好的用户体验。
4.1 scheduler
翻译成中文就是调度器,scheduler就是可以解决上述的两个优化点,scheduler会在内部创建自定义的各种队列来存放plug释放出来的request,然后scheduler对这个所有的request进行一个调度,按照特定的规则再分发给request_queue,让块设备处理。
4.2 架构图
4.3 小结
有了scheduler,我们就可以实现所有request的重新排序甚至合并,还可以对不同进程的request进行不同的优先级控制,目前linux支持的scheduler有:CFQ,deadline等
5、总结
学习Block Layer层,其实就是学习以上几个重要的部分:bio,request,plug,scheduler,request_queue,而且linux的single-queue架构就是和4.0版的架构差不多,目前手机上基本采用这个single-queue的架构。
linux目前除了支持single-queue,还支持multi-queue,我就不展开讲的,整体的结构很类似。
6、尾巴
整个流程好像看起来很爽,其实我们根本没有关注过进程的调度,我说的进程就是task_struct,多个进程同时发送bio,最后将bio变成request送给了scheduler进行调度,这些代码运行在各自的进程上,然后scheduler再将request分发给块设备处理这个又运行在另一个单独的进程上。如何让多个进程发完request之后休眠,如何让块设备驱动处理完之后唤醒这些休眠的进程。这个唤醒的关键点:在于bio结构体中的bio_end_io_t *bi_end_io这个callback函数指针。