天天看点

【原创】(六)Linux进程调度-实时调度器

<code>Read the fucking source code!</code> --By 鲁迅

<code>A picture is worth a thousand words.</code> --By 高尔基

说明:

Kernel版本:4.14

ARM64处理器,Contex-A53,双核

使用工具:Source Insight 3.5, Visio

在Linux内核中,实时进程总是比普通进程的优先级要高,实时进程的调度是由<code>Real Time Scheduler(RT调度器)</code>来管理,而普通进程由<code>CFS调度器</code>来管理。

实时进程支持的调度策略为:<code>SCHED_FIFO</code>和<code>SCHED_RR</code>。

前边的系列文章都是针对<code>CFS调度器</code>来分析的,包括了<code>CPU负载</code>、<code>组调度</code>、<code>Bandwidth控制</code>等,本文的<code>RT调度器</code>也会从这些角度来分析,如果看过之前的系列文章,那么这篇文章理解起来就会更容易点了。

前戏不多,直奔主题。

有必要把关键的结构体罗列一下了:

<code>struct rq</code>:运行队列,每个CPU都对应一个;

<code>struct rt_rq</code>:实时运行队列,用于管理实时任务的调度实体;

<code>struct sched_rt_entity</code>:实时调度实体,用于参与调度,功能与<code>struct sched_entity</code>类似;

<code>struct task_group</code>:组调度结构体;

<code>struct rt_bandwidth</code>:带宽控制结构体;

老规矩,先上张图,捋捋这些结构之间的关系吧:

【原创】(六)Linux进程调度-实时调度器

从图中的结构组织关系看,与<code>CFS调度器</code>基本一致,区别在与<code>CFS调度器</code>是通过红黑树来组织调度实体,而<code>RT调度器</code>使用的是优先级队列来组织实时调度实体;

<code>rt_rq</code>运行队列,维护了100个优先级的队列(链表),优先级0-99,从高到底;

调度器管理的对象是调度实体,任务<code>task_struct</code>和任务组<code>task_group</code>都是通过内嵌调度实体的数据结构,来最终参与调度管理的;

<code>task_group</code>任务组调度,自身为每个CPU维护<code>rt_rq</code>,用于存放自己的子任务或者子任务组,子任务组又能往下级联,因此可以构造成树;

上述结构体中,<code>struct rq</code>和<code>struct task_group</code>,在前文中都分析过。

下边针对RT运行队列相关的关键结构体,简单注释下吧:

运行时的统计数据更新,是在<code>update_curr_rt</code>函数中完成的:

【原创】(六)Linux进程调度-实时调度器

<code>update_curr_rt</code>函数功能,主要包括两部分:

运行时间的统计更新处理;

如果运行时间超出了分配时间,进行时间均衡处理,并且判断是否需要进行限流,进行了限流则需要将RT队列出队,并重新进行调度;

为了更直观的理解,下边还是来两张图片说明一下:

<code>sched_rt_avg_update</code>更新示意如下:

【原创】(六)Linux进程调度-实时调度器

<code>rq-&gt;age_stamp</code>:在CPU启动后运行队列首次运行时设置起始时间,后续周期性进行更新;

<code>rt_avg</code>:累计的RT平均运行时间,每0.5秒减半处理,用于计算CFS负载减去RT在CFS负载平衡中使用的时间百分比;

<code>RT调度器</code>与<code>CFS调度器</code>的组调度基本类似,<code>CFS调度器</code>的组调度请参考<code>(四)Linux进程调度-组调度及带宽控制</code>。

看一下<code>RT调度器</code>组调度的组织关系图吧:

【原创】(六)Linux进程调度-实时调度器

系统为每个CPU都分配了RT运行队列,以及RT调度实体,任务组通过它包含的RT调度实体来参与调度;

任务组<code>task_group</code>的RT队列,用于存放归属于该组的任务或子任务组,从而形成级联的结构;

看一下实际的组织示意图:

【原创】(六)Linux进程调度-实时调度器

请先参考<code>(四)Linux进程调度-组调度及带宽控制</code>。

<code>RT调度器</code>在带宽控制中,调度时间周期设置的为1s,运行时间设置为0.95s:

这两个值可以在用户态通过<code>/sys/fs/cgroup/cpu/rt_runtime_us</code>和<code>/sys/fs/cgroup/cpu/rt_period_us</code>来进行设置。

看看函数调用流程:

【原创】(六)Linux进程调度-实时调度器

<code>init_rt_bandwidth</code>函数在创建分配RT任务组的时候调用,完成的工作是将<code>rt_bandwidth</code>结构体的相关字段进行初始化:设置好时间周期<code>rt_period</code>和运行时间限制<code>rt_runtime</code>,都设置成默认值;

可以从用户态通过操作<code>/sys/fs/cgroup/cpu</code>下对应的节点进行设置<code>rt_period</code>和<code>rt_runtime</code>,最终调用的函数是<code>tg_set_rt_bandwidth</code>,在该函数中会从下往上的遍历任务组进行设置时间周期和限制的运行时间;

在<code>enqueue_rt_entity</code>将RT调度实体入列时,最终触发<code>start_rt_bandwidth</code>函数执行,当高精度定时器到期时调用<code>do_sched_rt_period_timer</code>函数;

<code>do_sched_rt_period_timer</code>函数,会去判断该RT运行队列的累计运行时间<code>rt_time</code>与设置的限制运行时间<code>rt_runtime</code>之间的大小关系,以确定是否限流的操作。在这个函数中,如果已经进行了限流操作,会调用<code>balance_time</code>来在多个CPU之间进行时间均衡处理,简单点说,就是从其他CPU的rt_rq队列中匀出一部分时间增加到当前CPU的rt_rq队列中,也就是将当前rt_rt运行队列的限制运行时间<code>rt_runtime</code>增加一部分,其他CPU的rt_rq运行队列限制运行时间减少一部分。

来一张效果示意图:

【原创】(六)Linux进程调度-实时调度器

来一张前文的图:

【原创】(六)Linux进程调度-实时调度器

看一下RT调度器实例的代码:

<code>pick_next_task_rt</code>函数是调度器用于选择下一个执行任务。流程如下:

【原创】(六)Linux进程调度-实时调度器

与<code>CFS调度器</code>不同,<code>RT调度器</code>会在多个CPU组成的<code>domain</code>中,对任务进行<code>pull/push</code>处理,也就是说,如果当前CPU的运行队列中任务优先级都不高,那么会考虑去其他CPU运行队列中找一个更高优先级的任务来执行,以确保按照优先级处理,此外当前CPU也会把任务推送到其他更低优先级的CPU运行队列上。

<code>_pick_next_task_rt</code>的处理逻辑比较简单,如果实时调度实体是<code>task</code>,则直接查找优先级队列的位图中,找到优先级最高的任务,而如果实时调度实体是<code>task_group</code>,则还需要继续往下进行遍历查找;

关于任务的<code>pull/push</code>,linux提供了<code>struct plist</code>,基于优先级的双链表,其中任务的组织关系如下图:

【原创】(六)Linux进程调度-实时调度器

<code>pull_rt_task</code>的大概示意图如下:

【原创】(六)Linux进程调度-实时调度器

当前CPU上的优先级任务不高,从另一个CPU的<code>pushable_tasks</code>链表中找优先级更高的任务来执行;

当RT任务进行出队入队时,通过<code>enqueue_task_rt/dequeue_task_rt</code>两个接口来完成,调用流程如下:

【原创】(六)Linux进程调度-实时调度器

<code>enqueue_task_rt</code>和<code>dequeue_task_rt</code>都会调用<code>dequeue_rt_stack</code>接口,当请求的rt_se对应的是任务组时,会从顶部到请求的rt_se将调度实体出列;

任务添加到rt运行队列时,如果存在多个任务可以分配给多个CPU,设置overload,用于任务的迁移;

有点累了,收工了。

【原创】(六)Linux进程调度-实时调度器

作者:LoyenWang

出处:https://www.cnblogs.com/LoyenWang/

公众号:<b>LoyenWang</b>

版权:本文版权归作者和博客园共有

转载:欢迎转载,但未经作者同意,必须保留此段声明;必须在文章中给出原文连接;否则必究法律责任

下一篇: C# 9.0 新特性

继续阅读