内核block层IO调度器—bfq算法简单调试总结

2023-11-11 14:50

本文主要是介绍内核block层IO调度器—bfq算法简单调试总结,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本文主要记录bfqq实际调试数据,主要涉及多进程IO传输时bfq调度器如何选择哪个bfqq的entity去使用。在看本文前,建议读者先下之前写的3篇文章《内核block层IO调度器—bfq算法之1整体流程介绍》、《内核block层IO调度器—bfq算法之2源码详解》、《内核block层IO调度器—bfq算法之3源码要点总结》,打个基础。本文基于centos 8.3,内核版本4.18.0-240.el8,详细源码注释见 https://github.com/dongzhiyan-stack/linux-4.18.0-240.el8。

 废话不多说,先看下bfqq的entity在bfq调度器st->active和st->idle 红黑树的排布(为了演示简单,没有演示entity在红黑树中的树形排列形式,只是演示entity按照entity->finish由小到大从左向右排列),如图:

一个进程派发IO请求都要先分配一个bfqq,然后把bfqq的entity按照entity->finish插入到st->acive 红黑树,entity->finish越小,entity在st->active红黑树越靠左,越容易被bfq调度器选中作为bfqd->in_service_queue,然后才能派发该bfqq上的IO请求。如果bfqq因bfqq没有要派发的IO请求了,则令bfqq过期失效而加入到st->ilde 红黑树。如果bfqq因为配额消耗光了,则令bfqq过期失效,然后是把bfqq重新加入st->active红黑树。

bfqq因配额消耗光而过期失效,函数流程是:__bfq_dispatch_request->bfq_select_queue()->bfq_bfqq_expire()->__bfq_bfqq_expire()->bfq_activate_requeue_entity()

  1. static void bfq_activate_requeue_entity(struct bfq_entity *entity,
  2.                     bool non_blocking_wait_rq,
  3. {
  4.       __bfq_activate_requeue_entity(entity, sd, non_blocking_wait_rq);
  5.       if (!bfq_update_next_in_service(sd, entity, expiration) &&!requeue)
  6.           break;
  7. }

bfqq因配额消耗光而过期失效,最后只是执行__bfq_activate_requeue_entity()把bfqq的entity重新加入st->active红黑树。只是呢?在st->active红黑树排列的靠后!然后执行bfq_update_next_in_service()从st->active红黑树查找最新的合适的entity赋于sd->next_in_service。之后回到bfq_select_queue()函数,执行bfq_set_in_service_queue(bfqd)-> bfq_get_next_queue(),再把最新找到的sd->next_in_service赋于sd->in_service_entity,这就是bfq调度器当前使用的entity。回到bfq_set_in_service_queue()函数再执行__bfq_set_in_service_queue(bfqd, bfqq),把sd->in_service_entity这个entity所属bfqq赋于bfqd->in_service_queue,这就是bfq调度器当前正在使用bfqq。

 bfq_update_next_in_service()函数中寻找最新的合适的entity赋于sd->next_in_service的过程是,bfq_update_next_in_service()->bfq_lookup_next_entity()->__bfq_lookup_next_entity()->bfq_first_active_entity(),最重要的代码是bfq_first_active_entity()中实现的,把它的源码再贴下:

  1. //st->active tree红黑树最左边查找entity->start最小并且entity->start小于vtimeentity
  2. static struct bfq_entity *bfq_first_active_entity(struct bfq_service_tree *st,
  3.                           u64 vtime) //vtime大部分情况是st->vtime
  4. {
  5.     struct bfq_entity *entry, *first = NULL;
  6.     struct rb_node *node = st->active.rb_node;
  7.     while (node)
  8.     {
  9.          entry = rb_entry(node, struct bfq_entity, rb_node);
  10. left:
  11.  printk("1:%s %d %s %d entry:%llx entry->start:%lld vtime:%lld pid:%d\n",__func__,__LINE__,current->comm,current->pid,(u64)entry,entry->start,vtime,bfq_entity_to_bfqq(entry)->pid);
  12.         if (!bfq_gt(entry->start, vtime))//entry->start < vtime if成立
  13.                first = entry;
  14.         //如果entry有左节点,一直向左查找,有靠左的entryentry->startentry->min_start越小
  15.         if (node->rb_left)
  16.        {
  17.              entry = rb_entry(node->rb_left,
  18.                      struct bfq_entity, rb_node);
  19.              printk("2:%s %d %s %d entry:%llx entry->min_start:%lld vtime:%lld pid:%d\n",__func__,__LINE__,current->comm,current->pid,(u64)entry,entry->min_start,vtime,bfq_entity_to_bfqq(entry)->pid);
  20.              if (!bfq_gt(entry->min_start, vtime)) {
  21.                    node = node->rb_left;
  22.                    goto left;
  23.               }
  24.         }
  25.         if (first)
  26.               break;
  27.         node = node->rb_right;
  28.     }
  29.     return first;
  30. }

本质就是在st->active红黑树最左边找一个entry->start最小并且entity->start小于st->vtime的entity。我们知道,st->active红黑树上的entity是按照entity->finish由小达大从左向右排列的,跟entry->start有什么关系?还不知道!感觉是个核心知识点。下文是在bfq_first_active_entity()中添加调试信息,打印涉及的变量及数据,如上bfq_first_active_entity()函数红色部分代码。

  • //1次找到 entry:ffff942b330eb888(绑定的进程pid 6067),但 entry->start > vtime 导致该entity不符合条件
  • 1:bfq_first_active_entity 1511 kworker/3:1H 487 entry:ffff942b330eb888 entry->start:14821446688905 vtime:14820509868269 pid:6067
  • //继续查找,找到entry:ffff942b330eb888 在红黑树再左边的节点 entry:ffff942b337ffc88,因 entry->min_start < vtime goto left分支
  • 2:bfq_first_active_entity 1519 kworker/3:1H 487 entry:ffff942b337ffc88 entry->min_start:14820400546738 vtime:14820509868269 pid:6070
  • //2次找到 entry:ffff942b337ffc88(绑定的进程pid 6070),但 entry->start < vtime 成立,找到第1个符合条件的entity
  • 1:bfq_first_active_entity 1511 kworker/3:1H 487 entry:ffff942b337ffc88 entry->start:14820437246898 vtime:14820509868269 pid:6070
  • //继续查找,找到entry:ffff942b337ffc88 在红黑树再左边的节点entry:ffff942b330e9288,因 entry->min_start < vtime goto left分支
  • 2:bfq_first_active_entity 1519 kworker/3:1H 487 entry:ffff942b330e9288 entry->min_start:14820437246898 vtime:14820509868269 pid:6071
  • //3次找到 entry:ffff942b330e9288(绑定的进程pid 6071),但 entry->start < vtime 成立,找到第2个符合条件的entity,终于找到st->active 最左边的entity
  • 1:bfq_first_active_entity 1511 kworker/3:1H 487 entry:ffff942b330e9288 entry->start:14820437246898 vtime:14820509868269 pid:6071

 显然,最后从st->active红黑树终于找到符合条件的entity:ffff942b330e9288。这段是我fio测试+正常文件读写时,有大概6个进程同时传输IO请求时出现的打印。很明显,此时st->active红黑树至少有PID是6067、6070、6071的进程所属的bfqq的entity,是一直向st->active 红黑树左边查找合适的entity:先是entity的start小于vtime,同时该entity如果在红黑树有左节点,该左节点的entity的min_start还必须大于vtime,该entity才是最终查找到的entity。为了便于理解,画了entity在st->active tree红黑树中的排列情况,如下:

vtime经测试一般都是st->vtime,即调度器的虚拟运行时间。如图,6067号进程 的entity:0xffff942b330eb888是st->active 红黑树的根节点,再向左边是6070和6071号进程对应的entity。可以发现, entity->start越小,entity在st->active红黑树越靠左。之前我们说过,entity按照entity->finish插入st->active红黑树时,entity->finish越小,在st->active红黑树越靠左。而entity->finish的计算规则可以简化成:entity->finish = entity->start + entity->budget/(entity->weight*N),如下看下源码:

  1. static void bfq_update_fin_time_enqueue(struct bfq_entity *entity,
  2.                     struct bfq_service_tree *st,
  3.                     bool backshifted)
  4. {
  5.      //根据entity->budget/entity->weight计算entity已经消耗的虚拟运行时间,entity->start加上这段虚拟运行时间得到entity->finish
  6.      bfq_calc_finish(entity, entity->budget);
  7.      //entity按照新的entity->finish插入st->active红黑树
  8.      bfq_active_insert(st, entity);
  9. }

因此,在配额entity->budget和权重entity->weight都是默认情况下,entity->finish和entity->start成线性正比关系。另外一个疑问点是就是entry->min_start,在bfq_first_active_entity()函数中查找合适的entity时,除了entity->start要小于vtime(调度器的虚拟运行时间),还要看该entity左节点的entity的entity->min_start是否大于vtime,大于vtime才能说明该entity是最终要找的合适的entity。这点还需要后续再摸索一下。

bfq调度算法比较复杂,关于entity->start、entity->finish、entity->min_start、st->vtime、entity->budget、entity->weight,以及entity在st->active红黑树的排列何更新规则,还需要多实践和总结。本文结束,如有错误请指出!

这篇关于内核block层IO调度器—bfq算法简单调试总结的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/390831

相关文章

Java中使用 @Builder 注解的简单示例

《Java中使用@Builder注解的简单示例》@Builder简化构建但存在复杂性,需配合其他注解,导致可变性、抽象类型处理难题,链式编程非最佳实践,适合长期对象,避免与@Data混用,改用@G... 目录一、案例二、不足之处大多数同学使用 @Builder 无非就是为了链式编程,然而 @Builder

在IntelliJ IDEA中高效运行与调试Spring Boot项目的实战步骤

《在IntelliJIDEA中高效运行与调试SpringBoot项目的实战步骤》本章详解SpringBoot项目导入IntelliJIDEA的流程,教授运行与调试技巧,包括断点设置与变量查看,奠定... 目录引言:为良驹配上好鞍一、为何选择IntelliJ IDEA?二、实战:导入并运行你的第一个项目步骤1

Java中的xxl-job调度器线程池工作机制

《Java中的xxl-job调度器线程池工作机制》xxl-job通过快慢线程池分离短时与长时任务,动态降级超时任务至慢池,结合异步触发和资源隔离机制,提升高频调度的性能与稳定性,支撑高并发场景下的可靠... 目录⚙️ 一、调度器线程池的核心设计 二、线程池的工作流程 三、线程池配置参数与优化 四、总结:线程

Spring Boot 与微服务入门实战详细总结

《SpringBoot与微服务入门实战详细总结》本文讲解SpringBoot框架的核心特性如快速构建、自动配置、零XML与微服务架构的定义、演进及优缺点,涵盖开发环境准备和HelloWorld实战... 目录一、Spring Boot 核心概述二、微服务架构详解1. 微服务的定义与演进2. 微服务的优缺点三

Java通过驱动包(jar包)连接MySQL数据库的步骤总结及验证方式

《Java通过驱动包(jar包)连接MySQL数据库的步骤总结及验证方式》本文详细介绍如何使用Java通过JDBC连接MySQL数据库,包括下载驱动、配置Eclipse环境、检测数据库连接等关键步骤,... 目录一、下载驱动包二、放jar包三、检测数据库连接JavaJava 如何使用 JDBC 连接 mys

Java中的雪花算法Snowflake解析与实践技巧

《Java中的雪花算法Snowflake解析与实践技巧》本文解析了雪花算法的原理、Java实现及生产实践,涵盖ID结构、位运算技巧、时钟回拨处理、WorkerId分配等关键点,并探讨了百度UidGen... 目录一、雪花算法核心原理1.1 算法起源1.2 ID结构详解1.3 核心特性二、Java实现解析2.

JavaSE正则表达式用法总结大全

《JavaSE正则表达式用法总结大全》正则表达式就是由一些特定的字符组成,代表的是一个规则,:本文主要介绍JavaSE正则表达式用法的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录常用的正则表达式匹配符正则表China编程达式常用的类Pattern类Matcher类PatternSynta

SQL中JOIN操作的条件使用总结与实践

《SQL中JOIN操作的条件使用总结与实践》在SQL查询中,JOIN操作是多表关联的核心工具,本文将从原理,场景和最佳实践三个方面总结JOIN条件的使用规则,希望可以帮助开发者精准控制查询逻辑... 目录一、ON与WHERE的本质区别二、场景化条件使用规则三、最佳实践建议1.优先使用ON条件2.WHERE用

基于Python实现一个简单的题库与在线考试系统

《基于Python实现一个简单的题库与在线考试系统》在当今信息化教育时代,在线学习与考试系统已成为教育技术领域的重要组成部分,本文就来介绍一下如何使用Python和PyQt5框架开发一个名为白泽题库系... 目录概述功能特点界面展示系统架构设计类结构图Excel题库填写格式模板题库题目填写格式表核心数据结构

IDEA如何实现远程断点调试jar包

《IDEA如何实现远程断点调试jar包》:本文主要介绍IDEA如何实现远程断点调试jar包的问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录问题步骤总结问题以jar包的形式运行Spring Boot项目时报错,但是在IDEA开发环境javascript下编译