关注了就能看到更多这么棒的文章哦~
Controlling realtime priorities in kernel threads
By Jonathan Corbet
原文来自:https://lwn.net/Articles/818388/
主译:DeepL
realtime scheduler class供开发者用来说明哪些任务具有最高的优先级,并保证在任何时候,最高优先级的任务都可以不受阻碍地利用CPU。内核本身执行的许多任务都要求能尽快完成,所以自然希望为执行这些任务的内核线程分配realtime优先级。但是,正如Peter Zijlstra在一个新的patchset中所论证的,在kernel内部分配这样的优先级是没有什么意义的;为了杜绝这种做法,他提议最大程度上取消内核线程自己进行优先级调整的能力。
在经典的realtime模型中,有两个schedule class:SCHED_FIFO和SCHED_RR。这两个class中,进程都有一个简单的整数来代表优先级。SCHED_FIFO 进程会持续运行,直到它们主动放弃CPU,接下来会选择优先级最高的进程先执行。而SCHED_RR则是让拥有最高优先级的所有可运行进程轮流运行,给每个进程一个固定的时间段。在这两个class中,实时优先级较低的进程都会被完全阻塞,直到所有优先级较高的进程都停下来为止,而无论优先级高低,这两个class中的进程都会优先于SCHED_NORMAL class中的进程执行,因为后者都是普通的非实时进程。
内核会将大量(并且在不断增加中)工作放置在内核线程中,这些线程是在内核地址空间中运行的特殊进程。这样做是为了让这些工作在系统调度器的控制下,不依赖任何其他线程。大多数内核线程运行在SCHED_NORMAL类中,必须与普通用户空间进程争夺CPU时间。但是,有一些kernel therad人们认为非常特殊,应该比用户空间进程要有更高优先级。因此他们采用了一种方法,就是将这些线程放到SCHED_FIFO class中。
但这时就会出现一个问题:如何决定某个线程应该设为什么优先级?要回答这个问题,就需要判断这个线程相对于所有以realtime优先级运行的其他线程是否更加重要,也要跟userspace的realtime工作比较一下谁更重要。这将是一个很难回答的问题,更何况在不同的系统和工作模式下这个答案很有可能还会各不相同。。一般来说,内核开发人员甚至不会做实验,而是直接选择一条路去实现。
Zijlstra认为,这种做法毫无意义:"内核本身不知道它对各种事情应该使用什么样的优先级,所以根本试都不用试(或者更坏的情况是试验下来反而结果会有副作用)。所以他改变了内核的内部函数接口,不再允许以特定SCHED_FIFO优先级运行。最后只剩下三个函数:
void sched_set_fifo(struct task_struct *p);
void sched_set_fifo_low(struct task_struct *p);
void sched_set_normal(struct task_struct *p, int nice);
对于loadable module来说,只有这3个函数可以用来操作线程的scheduling配置。这3个函数只被export到具有GPL兼容许可证的module中。调用sched_set_fifo()会将指定进程放到SCHED_FIFO类中,其优先级为50——这只是min和max之间的一半位置。对于需求不那么迫切的线程,sched_set_fifo_low()将优先级设置为最低值(1)。调用sched_set_normal()会将线程返回给定好的值SCHED_NORMAL类。
patchset中大部分代码都是在修改其他那些subsystem代码,让它们来使用最新API。所以这个patchset其实也刚好就总结了当前kernel中有哪些线程在使用SCHED_FIFO,总结如下:
Subsystem | Priority | Description |
---|---|---|
Arm bL switcher | 1 | The Arm big.LITTLE switcher thread |
crypto | 50 | Crypto engine worker thread |
ACPI | 1 | ACPI processor aggregator driver |
drbd | 2 | Distributed, replicated block device request handling |
PSCI checker | 99 | PSCI firmware hotplug/suspend functionality checker |
msm | 16 | MSM GPU driver |
DRM | 1 | Direct rendering request scheduler |
ivtv | 99 | Conexant cx23416/cx23415 MPEG encoder/decoder driver |
mmc | 1 | MultiMediaCard drivers |
cros_ec_spi | 50 | ChromeOS embedded controller SPI driver |
powercap | 50 | "Powercap" idle-injection driver |
powerclamp | 50 | Intel powerclamp thermal management subsystem |
sc16is7xx | 50 | NXP SC16IS7xx serial port driver |
watchdog | 99 | Watchdog timer driver subsystem |
irq | 50 | Threaded interrupt handling |
locktorture | 99 | Locking torture-testing module |
rcuperf | 1 | Read-copy-update performance tester |
rcutorture | 1 | Read-copy-update torture tester |
sched/psi | 1 | Pressure-stall information data gathering |
大家可以看到,内核开发者为线程选择的优先级值确实有许多差异。另外,drbd驱动使用的是SCHED_RR类,其中原因我们并不完全清楚。在Zijlstra的patchset中,所有使用优先级为1的代码都被转换为使用sched_set_fifo_low(),其余的地方则改为使用sched_set_fifo(),使其优先级都为50。
到目前为止,已经有一些补丁得到了回复,大部分都提供了Reviewed-by标签。看起来似乎很少内核开发者在自己driver中需要使用他们自己明确指定的SCHED_FIFO优先级值。因此,这个patchset合入后,不太可能会有严重反对。
不过,最终目标并不局限于内核内部的SCHED_FIFO值的清理。Zijlstra对SCHED_FIFO提出的一个反对意见是,即使开发者能够针对为他们的workload来选择完美的优先级值,但如果这个workload必须与另一个workload同时运行,而另一个workload又有自己指定的优先级值,那这两组值能自然而然地完美配合起来的可能性其实很小。
在当前的内核中,每一个使用SCHED_FIFO的realtime workload都会面临这个问题,因为为这个workload所选择的优先级必须也考虑其他说有内核线程的优先级。而其他线程的优先级定义其实不一定是仔细深思熟虑选择过的结果,也不一定有文档记录这个选择的原因。因此,让内核对SCHED_FIFO优先级的配置只有更少的选项,这应该会使realtime系统设计者更加轻松一些,他们不会介意选择受限的。
全文完
LWN文章遵循CC BY-SA 4.0许可协议。
欢迎分享、转载及基于现有协议再创作~
长按下面二维码关注,关注LWN深度文章以及开源社区的各种新近言论~