前言
EEVDF(Earliest Eligible Virtual Deadline First),翻译为最早合格的虚拟截止日期优先。
先看名字,有两个关键点:
- 合格的
- 最早虚拟截至日期
CFS调度算法根据其原理也可以解释为“最小虚拟时间优先”,因为其核心调度逻辑就是挑选vruntime最小的进程。而EEVDF现在将最小虚拟时间改为了最早虚拟截止日期,相同点就是都是用虚拟时间判断的,可以推断还是通过weight来对物理时间进行加权;不同之处就是用截至日期代替了之前的运行时间,截至日期从字面意思就可以判断其更强调调度延迟。运用了一部分deadline调度器的思想,选择运行截至时间最早的进程,以保证整个系统的响应度。
CFS虽然在大多数场景下都表现很不错,对所有进程都尽可能做到了“公平”,但其并没有与延迟有关的参数和判断逻辑,导致其无法判断一个进程是否是更紧急的。而对于现在的很多设备来说,延迟是用户最为关心的问题之一了,谁能解决这个问题,就能得到用户和开发者的认可,EEVDF应运而生。其实EEVDF并不是个新鲜的概念,早在1995年Ion Stoica和Hussein Abdel-Wahab就写了相关论文,详细介绍了其原理和核心逻辑。而后Peter Zijlstra在2023年将其实现并合入到了kernel-6.6主线上。
虽然说是EEVDF替代了CFS,成为最新的默认调度器,但其并不是完全与CFS不相关的全新调度器,而更像是对CFS的补充和完善,主要改动还是在fair.c等原cfs相关文件中。不过Zijlstra也说了:It completely reworks the base scheduler, placement, preemption, picking – everything,其核心逻辑都有发生变化,也因此才能推翻CFS伫立这么久的王朝吧~
原理
1. 滞后量
EEVDF只会挑选合格的进程来运行,他对“合格”是这么定义的:实际运行时间小于应得时间。用v代表应得时间,用v_i代表实际运行时间,可以得出:
v_i < v --> v - v_i > 0
v - v_i用一个变量来记录:lag,解释为滞后量。当滞后量为正数时,代表其运行时间小于应得时间,应该优先运行。而如果lag为负数,代表他已经用完了分配给他的时间,也就不应该继续运行,这样的进程被认为是不合格的,挑选下一个运行的进程时不会考虑他们。其中应得时间v是用所有进程的平均运行时间计算得到。
举例说明:
假设现在有三个优先级一样的进程A,B,C,刚开始时他们的lag都是0,随便挑选一个进程运行,例如:
- 让A运行30ms后
现在每个进程应得时间是:v = 30/3=10ms,
lag_A = 10 - 30 = -20ms
lag_B = 10 - 0 = 10ms
lag_C = 10 - 0 = 10ms - 现在A的lag < 0,已经不合格了,剩下B和C滞后值一样,我们再挑选一个进程B运行30s后:
v = (30 + 30) / 3 = 20ms
lag_A = 20 - 30 = -10ms
lag_B = 20 - 30 = -10ms
lag_C = 20 - 0 = 20ms - 现在只剩C的lag值是正数了,调度器下次就会选择C来运行。
2. 截止日期
虚拟截至日期的计算方式是进程达到合格的时间加上其在本周期获得的时间片。由此可见,时间片越短,虚拟截止时间越短,也就更容易被调度器选中。EEVDF的选择原则就是选虚拟截止时间时间早的进程。
所以想要获得优先调度权,就要设定相对较短的时间片。这也符合我们真实的系统情况:对延迟较为敏感的一般不需要很长的运行时间,比如鼠标键盘这种;而需要大量时间的程序一般对延迟要求不是很高,如后台下载、数据同步等。