在上篇文章中我们看到

if (plug) {

if (!request_count)

trace_block_plug(q);

else {

if (request_count >= BLK_MAX_REQUEST_COUNT) {//如果这个队列的个数太多了,就通过下面的blk_flush_plug_list进行泄洪

blk_flush_plug_list(plug, false);

trace_block_plug(q);

}

}

这个是在blk_queue_bio 中,也就是在提交请求的情况下,会调用blk_flush_plug_list 来unplug操作。

今天看看另外一种unplug操作,

调度时进行unplug(异步方式),

当发生内核调度时,当前进程sleep前,先将当前task的plug列表中的请求flush到派发队列中,并进行unplug。

主要代码流程如下:

schedule->

sched_submit_work ->

blk_schedule_flush_plug()->

blk_flush_plug_list(plug, true) ->注意:这里传入的from_schedule参数为true,表示将触发异步unplug,即唤醒kblockd工作队列来进行unplug操作。后续的kblockd的唤醒周期在块设备驱动中设置,比如scsi中设置为3ms。

queue_unplugged-> blk_run_queue_async

queue_unplugged():

void blk_flush_plug_list(struct blk_plug *plug, bool from_schedule)

{

struct request_queue *q;

unsigned long flags;

struct request *rq;

LIST_HEAD(list);

unsigned int depth;

flush_plug_callbacks(plug, from_schedule);

if (!list_empty(&plug->mq_list))

blk_mq_flush_plug_list(plug, from_schedule);

if (list_empty(&plug->list))

return;

list_splice_init(&plug->list, &list);

list_sort(NULL, &list, plug_rq_cmp);

q = NULL;

depth = 0;

local_irq_save(flags);

while (!list_empty(&list)) {

rq = list_entry_rq(list.next);list 里面存放的就是要执行的rq,每一个遍历出来。这个

list_del_init(&rq->queuelist);

BUG_ON(!rq->q);

if (rq->q != q) {

/*

* This drops the queue lock

*/

if (q)

queue_unplugged(q, depth, from_schedule);

q = rq->q;

depth = 0;

spin_lock(q->queue_lock);

}

/*

* Short-circuit if @q is dead

*/

if (unlikely(blk_queue_dying(q))) {

__blk_end_request_all(rq, -ENODEV);

continue;

}

/*

* rq is already accounted, so use raw insert

*/

if (rq->cmd_flags & (REQ_FLUSH | REQ_FUA))

__elv_add_request(q, rq, ELEVATOR_INSERT_FLUSH);

else

__elv_add_request(q, rq, ELEVATOR_INSERT_SORT_MERGE);

depth++;

}

static void queue_unplugged(struct request_queue *q, unsigned int depth,

bool from_schedule)

__releases(q->queue_lock)

{

if (from_schedule)

blk_run_queue_async(q);

else

__blk_run_queue(q);

spin_unlock(q->queue_lock);

}

根据from_schedule 进行同步操作或者异步

void blk_run_queue_async(struct request_queue *q)

{

if (likely(!blk_queue_stopped(q) && !blk_queue_dead(q)))

mod_delayed_work(kblockd_workqueue, &q->delay_work, 0);

}

这里面就调入一个异步时钟,到见了它还是会调用到__blk_run_queue,进行request的操作的。

这里面异步时钟的逻辑,它里面有一套,大概可以比喻成handle 的消息处理机制,这一套我们先不讨论。

我们先看下这个异步时钟是在哪里设置的,

blk_init_queue--》blk_init_queue_node--》blk_alloc_queue_node

就是在初始化的时候做的,

struct request_queue *blk_alloc_queue_node(gfp_t gfp_mask, int node_id)

{

struct request_queue *q;

int err;

.....

setup_timer(&q->backing_dev_info.laptop_mode_wb_timer,

laptop_mode_timer_fn, (unsigned long) q);

setup_timer(&q->timeout, blk_rq_timed_out_timer, (unsigned long) q);

INIT_DELAYED_WORK(&q->delay_work, blk_delay_work);

}

中间的时钟消息逻辑我们先不看,我们知道,到时间以后,它会执行blk_delay_work

static void blk_delay_work(struct work_struct *work)

{

struct request_queue *q;

q = container_of(work, struct request_queue, delay_work.work);

spin_lock_irq(q->queue_lock);

__blk_run_queue(q);

spin_unlock_irq(q->queue_lock);

}

OK,最终还是到__blk_run_queue,在进行下一步分析前我们先处理下逻辑。

就是一个list里面,有很多个bio请求,这些bio请求转换成驱动能认识的就是rq,什么时候对这个rq进行遍历,

一个就是实际请求的时候,同步策略,另外一个就是schedule的时候异步。

达到的条件是要达到MAX,我就开始遍历。每个rq。

rq的--->q,是由驱动设置进来的,q有可能有很多个驱动,如磁盘,usb或者其他block驱动

所以这里可以细化的,就是可以做一个list分类,每个驱动达到MAX,再来清理

那么我们作为学习,假设就是一个q来说,最终由__blk_run_queue,送入到那个地方进行运行,而驱动就使用elv_next_request,

拿取,rq。这里似乎还有点差距。我们在来看看__blk_run_queue,应该就没什么缝隙了。

void __blk_run_queue(struct request_queue *q)

{

if (unlikely(blk_queue_stopped(q)))

return;

__blk_run_queue_uncond(q);

}

inline void __blk_run_queue_uncond(struct request_queue *q)

{

if (unlikely(blk_queue_dead(q)))

return;

q->request_fn_active++;

q->request_fn(q);

q->request_fn_active--;

}

OK,最终调用的是request_fn,就是在

这边设置进来的东西,上面是我们的demo代码。

ok,调用到了驱动的代码,没有问题了,最终的这个elv_next_request,进行那q里面,电梯排好的请求。

这就意味着,电梯算法处理的是rq,它是怎么进入的,

进入也是没有问题的,

初始化也应该没有问题,这样的话,我大概就是可以去看,elv的代码了。

--- Linux文件系统学习系列笔记 ---

(原创笔记,转载请联系博主授权)

linux flush 文件,Linux文件系统学习:io的plug过程-blk_flush_plug_list的情况相关推荐

  1. linux flush 文件,Linux 写文件时 何时 flush buffer

    遇到了一个很奇怪的问题,读文件失败.尤其是想要读取特别小的文件的时候. 流程是: 数据流 -> 存入文件 -> 读出文件数据 -> 操作 开始怀疑文件没有保存下来. 仔细查看了log ...

  2. linux stat文件,Linux stat命令:显示文件或文件系统的详细信息

    在 Linux 中,文件有访问时间.数据修改时间.状态修改时间这三个时间,而没有创建时间.stat 是査看文件详细信息的命令,而且可以看到文件的这三个时间,其基本信息如下. 命令名称:stat. 英文 ...

  3. linux设备文件,Linux 文件系统与设备文件

    1 Linux 文件系统 1.1 Linux 文件系统与设备驱动关系 下图表明了 Linux 中虚拟文件系统.磁盘/Flash文件系统以及一般的设备文件与设备驱动程序之间的关系. 文件系统与设备驱动之 ...

  4. linux 编码文件,linux文件编码

    linux下新建一个文件,或采用fopen新建,那么文件的编码是什么? 怎么查看文件编码格式: 查看文件编码file命令 file ip.txt ip.txt: UTF-8 Unicode text, ...

  5. 分析 linux 日志文件,linux精讲|操作系统常见日志文件分析

    linux运维,离不开对系统日志的分析,除syslog外,还有常用的dmesg.wtmp.btmp.bash_history等系统日志文件以及应用程序相关的日志. 一.dmesg日志:记录内核日志信息 ...

  6. linux 移动 文件,linux移动文件命令

    linux移动文件命令 mv命令 功能:为文件或目录改名或将文件由一个目录移入另一个目录中.该命令如同DOS下的ren和move的组合. 语法:mv [选项] 源文件或目录 目标文件或目录 说明: 视 ...

  7. linux php文件,Linux php文件安装目录在哪

    php文件安装目录一般默认在"/var/www"里面,如果是自定义安装,则可以用Linux的命令进行查找,查找的命令是"find / -name "*www*& ...

  8. 怎么解压linux镜像文件,linux 怎么解压文件

    linux怎么解压文件 Linux提供了一个很简单的功能,用来解压*.zip 文件. 解压命令:unzip filename.zip Linux系统中还有一类后缀为*.tar的文件. 解压命令为: t ...

  9. linux flush 磁盘,linux 磁盘IO栈和优化思路

    首先来看一下整个的IO栈,大概分为7层,每一层都有其特殊的功能和意义. IO Layer 1.VFS层 即虚拟文件系统,这是linux内核为了应对不同的文件系统(ext3\ext4)抽象出来的,直接与 ...

最新文章

  1. 【转】MYSQL入门学习之十:视图的基本操作
  2. 《响应式Web设计全流程解析》一1.2 静态设计稿舒适区
  3. 2015人脸检测研究进展
  4. UART0串口编程(三):中断方式的串口编程;用中断编写发送函数
  5. baidu+app+per+android,百度移动统计|移动应用APP统计|android统计分析|iOS统计分析
  6. 使用@functools.wraps的理由
  7. 关于IOCP完成端口的文章
  8. django-oscar相关的模块调研信息汇总
  9. mysql的唯一索引UNIQUE
  10. html宽度满屏,宽度满屏的代码怎么样写?
  11. [POJ2104] 区间第k大数 [区间第k大数,可持久化线段树模板题]
  12. 游戏开发:Html5 虚拟摇杆控制人物移动
  13. kitti点云地图拼接
  14. idea运行sql文件
  15. 已知一点经纬度及与另一点距离和航向,求另一点经纬度
  16. 不懂就问,苹果电脑格式化了能恢复数据吗?
  17. VR光学设计的关键参数
  18. English Learning NetSource
  19. 仿腾讯QQ竖直滑动导航菜单
  20. Spring中的静态工厂与实例工厂

热门文章

  1. html图片滚动浏览,ul结合CSS制作网页相册滑动浏览效果
  2. 计算机软考可以直接高级吗,计算机软考没有中级能考高级吗
  3. 一年级下册数学计算机应用题,一年级数学下册期中检测试题
  4. 错误: 无法生成项目输出组“内容文件来自WebApplication1(活动)”
  5. 【洛谷】普及练习场 深度优先搜索【易】
  6. [Swift]LeetCode478. 在圆内随机生成点 | Generate Random Point in a Circle
  7. mysql 8.0.11安装教程
  8. 新浪sae平台进行数据库的连接
  9. 【循序渐进学Python】9.异常处理
  10. python类的属性和对象属性_Python打印对象的全部属性