cfs调度类深入解刨——最新内核细节分析2

上一篇《cfs调度类深入解刨------最新内核细节分析1》中将kernel 7.0及以上版本中(较于kernel 6.6)的基础概念做了一些总结,并且再次引出了"代理调度运行"的概念。

本篇文章讲述kernel 7.1版本中的"代理调度运行"设计用意及相关功能。

代理调度运行(SCHED_PROXY_EXEC)

延迟出列(sched_delayed)

系列文章列表

代理调度运行(SCHED_PROXY_EXEC)

任务优先级反转从 1991 年 Linux 诞生之初、由于支持多任务和进程同步锁就原生存在的经典理论缺陷。举个经典的例子,高优先级任务被低优先级任务阻塞,导致中优先级任务比高优先级任务先执行的现象:

  1. 低优先级(任务A):如获取了共享内存的总线锁(互斥锁)。
  2. 高优先级(任务C):需要获取同一个总线锁,因此被挂起并等待任务A释放锁。
  3. 中优先级(任务B):它不需要锁,但它的优先级高于任务A,因此它抢占了任务A的CPU时间。

Linux 2.6.18 版本中,内核正式合入了 RT-Mutex(实时互斥锁)机制和 PI-Futex(优先级继承 Futex),当高优先级任务C阻塞于低优先级任务A持有的锁时,任务A临时提升为任务C的优先级,从而避免中等优先级的任务B抢占,让任务A快速执行完并释放锁。这首次在上游代码中预防了实时任务的ABC反转,但大部分服务器内核未开启/未使用RT-Mutex等机制(成本代价偏高,并且cfs基本调度类的任务还可以靠某个时刻检测,持锁顺延等手段),因此各家公司内部流传着自己的治理手段。

EEVDF到来的时代,新型服务器逻辑cpu数量已经达到512核心,更多的容器及任务数量,更复杂的迁移场景(如任务驱逐形式迁移,高优先级调度类任务频繁抢占等),任务优先级反转成为不可忽视并且持续演进的死锁问题。

7.0及以上版本中(kernel 6.17中增加),调度代理运行(SCHED_PROXY_EXEC)功能已经愈发稳定,执行逻辑如下(__schedule流程):

  1. 选取下一个任务。
  2. rq队列下一个调度类赋值为下一个任务调度类。
  3. rq队列贡献任务(donor)赋值为下一个任务。
  4. 下一任务属于阻塞状态,从贡献任务向互斥锁的所属者(owner)遍历(找出哪个任务拿到的互斥锁):
    4.1. 互斥锁没有所属者,任务锁阻塞状态清空(未持有互斥锁或者在非预期的互斥锁上阻塞则打印一次警告),贡献任务可以运行了,返回贡献任务。
    4.2. 所属者不在rq队列中或者所属者的调度实体支持延迟出列(sched_delayed),rq队列贡献任务赋值为rq队列空闲任务(互斥锁持有者偷懒了,贡献任务只能先放一放等互斥锁持有者让出锁了再说,并且当前只能让空闲任务运行避免锁过久等待,优先级反转等问题),设置重新调度标志并返回rq队列空闲任务。
    4.3. 所属者所在的cpu不是当前cpu,当前rq队列设置重新调度标志并返回rq队列空闲任务,将贡献任务迁移到所属者所在的cpu队列中(贡献任务在这里没啥用了,去所属者的cpu吧,等他让出锁就解放了。但是在7.0版本中还是上一个想法,贡献任务还是等互斥锁持有者让出锁了再说,说明7.1版本确实进化了,与其等待不如加入)。
    4.4. 所属者包含正在被迁移标志,设置重新调度标志并返回rq队列空闲任务(所属者也不保了,等他稳定了再说)。
    4.5. 所属者不在rq队列中或者所属者所在的cpu不是当前cpu,返回null(重试吧,所属者可能发生了变动)。
    4.6. 所属者是贡献任务,设置重新调度标志并返回rq队列空闲任务(???ttwu_runnable发力的时刻到了,调度rq->idle,以便ttwu_runnable()能够获取rq锁,并将所有者标记为运行状态)。
    4.7. 返回所属者(理想的场景,锁到擒来)。

上面的调度代理运行可以看出这个功能只为了一件事,就是当前待运行的任务没有拿到锁,必须想尽一切办法让所属者先运行并让出锁,这段期间cpu运行空闲任务浪费cpu也能接受(出现这种锁等待场景说明硬件也快到达处理瓶颈了),毕竟死锁代价远远大于等待时间。

延迟出列(sched_delayed)

配合代理调度运行(SCHED_PROXY_EXEC)出的功能,当任务因为互斥锁阻塞等待时不用移出队列(包含DEQUEUE_SLEEP标志,不包含延迟出列或出列限流标志,并且没有运行资格),再次执行入列函数时需要先移出队列再加回队列,更新cfs_rq队列负载等。更新调度实体虚拟滞后值时,在开启DELAY_ZERO的情况下,调度实体的虚拟滞后值最大为0,避免窃取运行机会(理论上已经运行了足够的时间)。

延迟出列包括以下几种重要信息:

  1. 尝试唤醒函数(ttwu)和__sched_fork函数检测到任务具有延迟出列(sched_delayed = 1)打印警告。
  2. 选取下一个任务时,cfs_rq队列的next具有延迟出列(sched_delayed = 1)打印警告。
  3. 再次入列(requeue_delayed_entity)不具有延迟出列(sched_delayed = 0)打印警告。
  4. cfs抢占函数中具有延迟出列(sched_delayed = 1)的任务不具备抢占能力。
  5. 任务具有延迟出列(sched_delayed = 1)时,cfs_rq队列的层级可运行数量不包括这个任务。

系列文章列表

《cfs调度类深入解刨------核心结构的用意》

《cfs调度类深入解刨------核心结构细节分析》

《cfs调度类深入解刨------最新内核细节分析1》

相关推荐
A小辣椒2 小时前
TShark:基础知识
linux
AlfredZhao4 小时前
OCI 明明分配了 200G 系统盘,为什么 df 只看到 30G?
linux·oci
AlfredZhao19 小时前
vi 删除指定范围的行,不用再反复按 dd
linux·vi
用户9718356334661 天前
银河麒麟 KY10 申威(SW64) 安装 nginx-1.16.1-2.p01.ky10.sw_64.rpm 详细步骤
linux
猪脚踏浪1 天前
linux 拷贝文件或目录到指定的位置
linux
大树882 天前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠2 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
bush42 天前
嵌入式linux学习记录十四、术语
linux·嵌入式
载数而行5202 天前
Linux 11 动态监控指令top
linux
小宇宙Zz2 天前
Maven依赖冲突
java·服务器·maven