关注了就能看到更多这么棒的文章哦~
Scheduling for asymmetric Arm systems
By Jonathan Corbet
November 30, 2020
DeepL assisted translation
https://lwn.net/Articles/838339/
Arm 处理器架构在很多方面都在推陈出新,其中有一些功能就需要对内核进行重大改变。例如,big.LITTLE 架构,把快的(但同时也会更耗电的)和慢的(但更省电的)CPU 放在同一个 System-on-chip(SoC)中。为了让 Linux 能够在这种系统上正确地对任务进行调度,就需要对调度器进行大的改动。尽管 big.LITTLE 有很多看起来很奇怪的地方,但它们的 CPU 在某种意义上来说仍然是相同的:系统中的任何任务都可以在这些 CPU 上运行。不过,如果调度器面临的是一个不再能保证这一点的系统时,调度器该怎么做呢?
Linux 上的多处理器(Multiprocessor)支持诞生于对称多处理(symmetric multiprocessing)的时代。在这种系统中,从名字就可以看出,所有的 CPU 都是基本上相同的。任一个 CPU 都可以以基本相同的性能来运行任务。在 SMP 系统上,调度器主要关注的是让所有的 CPU 都忙起来。虽然 cache effect 和 NUMA locality 这两点决定了调度器不应该鼓励在 CPU 之间进行任务迁移,但任一个任务选择具体哪一个 CPU 来执行,这通常是一个无所谓的问题。
Big.LITTLE 将具有不同性能特征的 CPU 捆绑在一起,这样一来就改变了这个假设。因此,为每个任务选择哪个 CPU 就变得更加重要了。如果把任务放在错误的 CPU 上,可能会导致性能不佳或功耗过高,因此在 big.LITTLE 系统上对工作负载分配进行优化,这已经有了大量的工作了。不过当调度器出错的时候,虽然性能会受到影响,但任务还是可以能正常执行下去的。
不过未来的 Arm 设计中,系统中可能有一些 CPU 可以同时运行 64 位和 32 位任务。而其他 CPU 则只能运行 64 位任务。这种设计的优点是可以减少 32 位运算相关的那部分芯片面积,毕竟许多系统中可能永远不会真正使用 32 位程序。同时,偶尔需要运行 32 位程序的话,系统仍然有这个能力。不过,这个代价是在我们建立的系统中,有一些 CPU 根本无法运行某些(也就是 32 位程序)任务。调度器如果选择不正确的话,结果不再仅仅是性能影响了,而是会对涉及到的工作负载产生灾难性的后果。
Qais Yousef 在 10 月份发出一组 patch 来试图解决这个问题,随后的讨论焦点主要集中在如果一个 32 位任务试图在一个只有 64 位的 CPU 上运行时,应该有什么后果。Yousef 最初打算让内核直接 kill 这样的任务,但是也准备了一个 patch 作为备选,其中会重新计算任务的 CPU affinity mask(一个用户可以设置的 bitmask,表示任务可以在哪些 CPU 上运行),改为只允许在 32 位的那些 CPU 上执行。他说,如果用户空间能够被信任的话,只要它正确设置了 32 位任务的 CPU affinity,那么最后这个 patch 就没有必要了。
Scheduler 的维护者 Peter Zijlstra 回应说,这种调整 affinity-mask 的改动 "不会发生"。他说,这个 bitmask 是在 user-space 的控制下,不应该被内核来直接修改。Will Deacon 补充说,内核不应该试图把自己系统的不对称特征向用户空间隐瞒。"我更乐意让调度器做自己的事情,如果这些 32 位任务的某一个最终出现在一个无法处理它的内核上,那算它倒霉,会直接被 kill"。
10 月底,Deacon 发布了一套自己的 patch,解决了他在 Yousef 的实现中看到的一些问题。它去掉了 affinity-mask 的操作,而是直接 kill 那些试图在无法支持自己的 CPU 上运行的任务。为了帮助用户空间正确设置 affinity masks,该 patch 还增加了一个 sysfs 文件,用来公告哪些 CPU 可以运行 32 位任务。
不过到 11 月中旬这个系列的 patch 演进到第 3 版时,这个行为已经发生了变化。如果一个 32 位任务试图在一个只有 64 位的 CPU 上运行,它的 affinity mask 会像 Yousef 的第一个补丁一样被修改。区别在于,如果原来的 affinity mask 中没有 32 位的 CPU,那么这时 bitmask 会被完全归零,使得此任务没有 CPU 可以运行。在这种情况下,将使用一个 fallback mask,具体的定义是根据不同体系架构来定的,但在 Arm 上(这是目前唯一需要此功能的架构),fallback mask 包含所有可以运行 32 位任务的 CPU。这可以起到使任务能在其原来的 affinity mask 之外的 CPU 上运行的效果。
Zijlstra 质疑这个改动,为什么不直接 kill 掉那些错误任务。"我以为我们可以接受… … 用户做了蠢事,那么就直接被 SIGKILL。有什么原因导致这里又改变了?" 后来了解到,这样修改的原因在于这种场景:当一个 64 位任务在 64 位 CPU 上运行并且调用 execve()来运行一个 32 位程序时应该如何处理。64 位的程序代码很可能不知道新的可执行文件与当前 CPU 不兼容,所以很难指望这个 64 位任务能正确地设置 CPU affinity。新运行的程序甚至没有机会运行 sched_setaffinity()来解决问题,即使它知道自己应该是只能在那些 32 位 CPU 上运行。事实上,当人们发现问题的时候,它甚至无法处理 SIGKILL 信号。Deacon 不打算去支持这种情况,而是决定在有必要的时候直接覆盖 affinity mask。
这样做的结果可以说是违反了内核的 ABI 规则,因为它明明说了 CPU-affinity mask 应该在 execve()调用中得到继承(而不是被内核自己修改掉)。正如 Marc Zyngier 所指出的,另一种改法"一定会导致系统不可靠"。修改 ABI rule 应该是比起让系统变得不可靠来说更合适的解决方案了,哪怕其他问题可以得到解决。
所以,有可能的最终合入 mainline 的会是这种改法的一些变体。Yousef 赞同 Deacon 的做法,他说。"我唯一担心的是,这种方法太棒了,会导致采用这种 design 的 SoC 很快变得越来越多"。这种硬件最终能普及到什么程度还有待观察,但等它出现的时候,Linux 应该已经准备好了。请继续关注 CPU 设计者们想象出来的下一个有趣的不对称方案会是什么。
全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。
欢迎分享、转载及基于现有协议再创作~
长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~

本文探讨了Linux内核如何适应Arm设计的新变化,如混合架构中的32位和64位CPU。Scheduling问题变得更复杂,涉及任务放置、性能优化和错误处理。开发者们正在讨论如何处理32位任务在仅支持64位的CPU上运行的情况,包括是否直接kill任务或调整affinity-mask策略。最终可能的解决方案可能会影响系统的可靠性和ABI规则。
844

被折叠的 条评论
为什么被折叠?



