LWN:为用户空间的加锁情况延长时间片!

关注了就能看到更多这么棒的文章哦~

Extending time slices for user-space locks

By Daroc Alden
February 19, 2025
Gemini-1.5-flash translation
https://lwn.net/Articles/1009509/

Steven Rostedt 最近发布了一个补丁集,该补丁集可以通过让调度器更多地了解何时可以安全地中断某些用户空间应用程序,来帮助提高它们的性能。该补丁集允许程序在被中断之前请求一个小的缓冲窗口,以便它们可以放弃任何锁,从而减少其他线程花费在等待上的时间。Rostedt 分享的性能数据显示,对于某些程序来说,该补丁可能会将获取锁所花费的时间减少一半,尽管由于他的测试是专门针对这种情况进行调整的,因此现实世界的项目应该预期改进幅度会略小一些。此更改遭到了调度器维护者 Peter Zijlstra 的反对,他反对该补丁集的方法。

背景

调度器的核心工作是决定在给定的时间,哪个任务应该在每个 CPU 上运行。为了执行其决策,它会在不同的时间点抢占正在运行的任务——当一个任务已经运行了其分配的时间片时,当一个硬件中断到达时,当一个更高优先级的任务需要运行时,等等。当一个任务在用户空间运行时,抢占它相对简单。但是抢占内核代码需要更多的协作。

Linux 内核有几个不同的抢占级别,用户可以选择,从 PREEMPT_FULL (完全抢占,可以在几乎任何时候中断内核代码)一直到 PREEMPT_NONE (完全不抢占,除了在显式的抢占点之外,从不中断内核代码)。2024 年,内核获得了一个新的级别, PREEMPT_LAZY (延迟抢占)。此设置使得当一个事件可能通常会导致另一个线程抢占正在运行的线程时,它可以只设置一个标志。当正在运行的线程返回到用户空间,或者当下一个调度器时钟节拍 (scheduler tick) 发生时,内核会查看该标志以决定是否重新调度正在运行的线程。

当然,情况会更复杂。调度器时钟节拍、硬件中断和实时线程都以略有不同的方式影响抢占。LWN 已经报道了这些年来的细节。但总的来说, PREEMPT_LAZY 结合了 PREEMPT_FULL 和 PREEMPT_NONE 的最佳性能特性——正在运行的线程 可以 在几乎任何时候被抢占(允许低延迟的实时进程),但大多数情况下不会。在典型情况下,线程将完成其当前的任务,并且仅在返回到用户空间或其时间片结束时才被抢占,这更有利于性能。

特别是,延迟抢占允许内核将抢占推迟到任务无论如何都需要进行上下文切换时,从而避免不必要的切换,进而提高性能。但是,关于是否让任务继续运行的决定只能通过内核拥有的信息来做出。如果一个线程使用纯用户空间的锁,例如自旋锁 (spinlock),那么抢占它可能会要求其他线程浪费时间等待该线程再次被调度。

因此,Rostedt 的补丁集为用户空间程序提供了一种方式来向内核发出信号,表明它们处于临界区;它向结构体添加了一个字段,该结构体用于可重启序列(restartable sequences),内核在抢占程序时会检查该字段。此结构体被映射到内核内存和用户空间程序的内存中,因此要让内核更新也并不需要承担系统调用的开销。如果程序表明它处于临界区并且发生了抢占事件,内核将检查抢占的原因是否是可以稍微延迟的(根据 PREEMPT_LAZY 的标志)。如果是这样,调度器将等待一小段时间,以便程序可以放弃其锁。

内核还使用相同的共享内存来告诉用户空间进程它已经收到了缓冲期,并且一旦它离开了临界区,就应该发出一个系统调用(任何系统调用)来让内核调度下一个任务。如果一个不合作的用户空间程序不这样做,调度器将在 50µs 后正常抢占该程序。为了换取这短暂的延迟,该补丁集显著减少了其他 CPU 上的线程花费在等待用户空间锁上的时间。虽然确切的加速将取决于应用程序,但 Rostedt 确实分享了一些性能数据,其中等待锁所花费的时间减少了一半。

在他的补丁理由中,Rostedt 明确表示,这不旨在启用用户空间程序尚未拥有的任何新能力;这纯粹是低级用户空间锁的性能优化。内核不需要尊重进程的更多时间请求。该信息仅被视为提示,以使其能够更有效地调度任务。

讨论

Zijlstra 不喜欢这个补丁集,他说:“我仍然完全讨厌这种方法。”尽管有这样一个令人沮丧的开端,Rostedt 询问他希望如何实现这个想法,从而促使 Zijlstra 详细说明。他更喜欢一种适用于所有抢占模式的实现,而不仅仅是 =PREEMPT_LAZY=。

Rostedt 说,如果这个特性是为所有抢占模式实现的,那么可能会导致这样一种情况:一个事件导致内核中的一个任务被重新调度,但是同一个事件不会导致用户空间任务被重新调度。尽管他没有明确说明,但这意味着这种情况实际上没有意义。他还解释说,他的最终目标是将这种性能增强应用于虚拟机。

Zijlstra 回应了一系列抱怨,其中最重要的是 PREEMPT_LAZY 目前既不是默认的抢占方法,也不是推荐的抢占方法。他说,即使它最终成为默认设置,也始终会有 PREEMPT_FULL 作为一种选择。因此,任何基于所选抢占模式的方法都不适用于大多数工作负载。

Rostedt 没有看出这有什么问题,他说,如果大多数程序必须等待 PREEMPT_LAZY 成为默认设置才能从中受益,那也没关系。此外,启用 PREEMPT_FULL 的用户正在尝试最大限度地减少延迟——因此可能根本不希望使用此功能。

Suleiman Souhlal 问为什么 Rostedt 的补丁集需要 =PREEMPT_LAZY=;Rostedt 解释说,该代码需要某种方式来区分可以容忍额外延迟的抢占事件(例如到达时间片的末尾)与实时线程的抢占。“我们不应该延迟任何 RT 任务”,他说。在讨论的最后,他甚至分享了一个想法,即如何通过更改内核为不同事件设置的标志,将来可能会将他的补丁集扩展到其他抢占模式。

然而,实时线程不应被延迟的想法被证明是与 Zijlstra 争论的另一个点,Zijlstra 不认为对实时任务添加一个小的、有界延迟有什么问题。他解释说,内核中已经有一些地方可以持有锁 50µs,并且不认为在用户空间中再添加一个会导致问题:

There is no difference between a 'malicious/broken' userspace consuming the entire window in userspace (50us, 20us whatever it will be) and doing a system call which we know will cause similar delays because it does in-kernel locking.

Rostedt 仍然坚持认为,在实时任务上添加任何额外的延迟是完全不可接受的,并且拒绝以这种方式更改补丁集。他们两人就此话题交换了更多的电子邮件,但未能达成协议。Rostedt 还与 Joel Fernandes 进行了长时间的交流,内容相同。

这并不是第一次提出允许用户空间向调度器提供更多关于锁定的信息的想法。Zijlstra 的抱怨之一是,他更喜欢 Prakash Sangappa 在几个月前分享的方法——尽管他对该补丁集的实现方式也有很多不同意的地方。它具有类似的效果,但是使用新的共享内存映射来实现内核和用户空间之间的通信,并且以稍微不同的方式与调度代码集成。

虽然 Zijlstra 似乎赞成使用一种机制来更好地调度使用用户空间锁的线程的总体思路,但他认为到目前为止,关于该主题的任何补丁都不是应该采用的方式。另一方面,他没有明确拒绝任何一个补丁集,因此 Rostedt 和 Sangappa 可能会继续开发他们各自的补丁集。随着时间的推移,这样的改变可以为虚拟机、绿色线程库和其他使用自旋锁的用户空间程序提供显著的性能优势。如果 Zijlstra 如愿以偿,这种改变甚至可能使具有除 PREEMPT_LAZY 之外的抢占模式的内核受益。

全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。

欢迎分享、转载及基于现有协议再创作~

长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~

1fcc3c2d785c40728e973b3d22c8c466.jpeg

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值