LWN:用户空间临界区相关的延后调度处理!

本文讨论了用户空间编程中如何通过StevenRostedt的补丁实现延迟抢占,以改善自旋锁在存在竞争时的性能,尤其是在处理临界区时。作者探讨了这一技术与内核懒惰抢占的相似性,以及它如何避免因抢占导致的性能损失。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

Deferred scheduling for user-space critical sections

By Jonathan Corbet
October 27, 2023
ChatGPT translation
https://lwn.net/Articles/948870/

User-space 开发人员在处理深度多线程化的应用程序时,通常希望能够使用自旋锁(spinlock)来保护共享数据结构免受并发访问。然而,对于用户空间自旋锁(user-space spinlocks)存在一个基本问题:无法阻止线程被抢占。尽管已经尝试了各种解决这个问题的方法,但是Steven Rostedt提出的这个补丁对许多解决方案的前提发出了自己的疑问:如果有可能至少在短时间内阻止抢占呢?

自旋锁在没有竞争(contention)的情况下速度很快,并且在不需要内核帮助的情况下就可以获取。然而,在存在竞争的情况下,情况就复杂了。在这种情况下,普通的自旋锁实现会"自旋",即重复去轮询(polling)锁的状态,直到可以获取到这个锁。但是如果锁的持有者被抢占并且没有运行,那么自旋动作可能就会无限期地进行,浪费 CPU 时间。更糟糕的是,自旋的线程可能就是抢占了持有锁的线程;在这种情况下,主动自旋反而会使得锁无法被释放。总之,在运行的线程无法执行自旋的情况下,对持有者为非运行线程的锁进行自旋可能会破坏系统的性能。

使用内核的 futex 机制时,等待锁的线程将调用进入内核并进入睡眠状态,直到锁被释放;这种方法可行,但是系统调用的成本可能是难以被接受,哪怕在相对较快的 Linux 上也是如此。自适应自旋的工作正在进行中,其中一个线程仅在锁的持有者当前正在运行时才会进行自旋;这应该使得自旋锁在大多数情况下纯粹位于用户空间。但是,如果被抢占的锁持有者仍然可能会强制调用进入内核,从而妨碍系统整体上的正常运行。让锁持有者持续运行直到释放锁(正如在大多数内核配置中所做的那样)的方式会更有效,可以避免这个问题,但是让非特权线程有机会改变调度器的决策本身可能会引发一系列问题。

Rostedt 认为有一种方法,可以允许线程请求更多的 CPU 时间来完成临界区(critical section),这是基于内核社区目前正在讨论的懒惰抢占(lazy-preemption)的思想。它跟自适应自旋(adaptive-spinlock)工作类似,是基于可重启序列(restartable sequence)功能的,该功能似乎正在演变成一种与调度程序低成本通信的机制。

Rostedt 的补丁向用户空间和内核之间共享的 rseq 结构添加了一个新字段 cr_flags 。如果正在运行的线程设置了最低 bit(名为 RSEQ_CR_FLAG_IN_CRITICAL_SECTION ),则表示线程当前正在临界区里运行,并希望能够在更长时间内不被中断地运行。调度器在抢占该线程之前会检查这个标志;如果此标志已经被设置了,调度程序将把抢占该线程的动作推迟一小段时间,方式类似于内核中的懒惰抢占(lazy preemption)。这个推迟动作不是必然会发生的;例如,在需要运行实时任务的时候还是会占有 CPU 的,无论这个 bit 的设置是什么。但是在没有这类任务的情况下,内核将允许处于临界区的线程运行。

然而,如果内核以这种方式推迟了抢占,那么就会在 cr_flags 中设置 RSEQ_CR_FLAG_KERNEL_REQUEST_SCHED 这个 bit。当线程退出其临界区时,它应该检查该标志,如果该标志已设置,则线程应该进行系统调用,让内核切换到它真正想要运行的任务。如果线程在下一个计时器滴答(timer tick)发生时仍在运行,则无论如何它都会被抢占。

这个功能可能看起来像是一个可以使得恶意线程有机会获取更多可用 CPU 时间的方法,只要直接保持设置临界区相关的 bit 从而尽可能多地推迟抢占。然而,6.6内核发布中将会出现的EEVDF调度程序将注意到线程获取了超出其份额的 CPU 时间,并在此后对其进行惩罚。因此,不应该会有通过这种机制获取更多的总时间,并且甚至有方法可能不需要添加一种检测试图以这种方式滥用推迟的线程。

推迟抢占(deferred preemption)功能如果合并的话,将不会消除自适应自旋(或类似的东西)的需求;锁持有线程仍然可能被抢占。但是它应该可以提高使用自旋锁的应用程序的性能,也许会很显著。当锁持有线程被抢占时,任何需要该锁的其他线程也将被阻塞,这增加了抢占的成本。通过将抢占移至不会影响其他线程的时间,这个功能就可以避免这种代价。Rostedt 进行了一个简单的基准测试,似乎证明了这一点:“在这 5 秒内,它几乎可以进入临界区 100 多万次!这是一个高达 23% 的提高!”他认为推迟抢占对于在 guest 内核中实现自旋锁可能也是有用的。

然而,这个功能是否会合并目前来说还是悬而未决的;调度程序维护者 Peter Zijlstra 对此并不热衷。他担心无论多长的延迟都不会让人满意,推迟抢占可能会导致引出延迟 latency 问题,该机制仍然不可靠(应用程序不能指望必定会推迟抢占),以及过度强调使用系统调用来处理锁竞争的成本。Rostedt 回应说,对调度程序决策的干扰应该是最小的,并且实际经验表明在这些情况下系统调用的开销仍然太昂贵了。当推迟抢占时任务所花费的额外时间,他说,与进行长时间系统调用时可能发生的情况,是一样糟糕的。这个功能或者从中演变出来的进一步的功能是否能够进入 mainline 内核,只有时间才能告诉我们。它有可能显著提高某些类型的性能非常重要的应用程序。然而,可能需要更广泛的测试来证明它不能被用来破坏其他系统的性能。这组 patch 相对较小,但是围绕它的讨论可能还会持续一段时间。

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

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

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

format,png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值