conditional_wait() 在内核和 hardware/assembly 级别是如何实现的?

How is conditional_wait() implemented at the kernel and hardware/assembly level?

我了解等待条件变量的线程自动释放锁并进入睡眠状态,直到被来自另一个线程的条件信号唤醒(当满足特定条件时)。唤醒后,它自动重新获取锁(不知何故神奇地)并根据需要更新并解锁临界区。

如果有人能解释一下这个 conditional_wait() 过程是如何在内核和 hardware/assembly 级别实现的,那就太好了?

如何以原子方式释放和重新获取锁?内核如何确保它?

睡在这里到底是什么意思?这是否意味着上下文切换到另一个 process/thread?

在线程休眠期间,这个线程如何通过信号在内核级别实现唤醒,是否为这些机制提供了任何特定于硬件的支持?

编辑:

看来 "futex" 就是管理这些 wait/signal 东西的人。缩小我的问题范围: futex系统调用等待和通知条件变量是如何在底层implemented/works的?

在高层次上(既然你在问这个问题,那么高层次就是你所需要的)它并不那么复杂。首先,您需要了解责任的层次。基本上有3层:

  • 硬件级别 - 通常可以在单个 ASM 指令中编码
  • 内核级别 - OS 内核所做的事情
  • 应用程序级别 - 应用程序所做的事情

通常,这些职责不会重叠 - 内核不能做只有硬件可以做的事情,硬件不能做只有内核可以做的事情。考虑到这一点,记住当谈到锁定时,很少有硬件知道它是很有用的。它几乎可以归结为

  • 原子算术 - 硬件可以锁定特定的内存区域(确保没有其他线程访问它),对其执行算术运算并解锁该区域。这只能适用于芯片本身支持的算法(没有平方根!)和硬件本身支持的大小
  • 内存屏障或栅栏 - 即在指令流中引入屏障,这样当 CPU 重新排序指令或使用内存缓存时,它们不会越过这些栅栏并且缓存会是新鲜的
  • 条件设置(比较并设置)- 如果内存区域是 B,则将内存区域设置为值 A 并报告此操作的状态(是否设置)

CPU 几乎可以做到这些。如您所见,这里没有 futex、mutex 或条件变量。这些东西是由具有 CPU 支持的操作的内核制作的。

让我们从更高的层次看一下内核如何实现 futex 调用。实际上,futex稍微复杂一些,因为它是用户级调用和内核级调用按需混合的。让我们看看 'pure' 互斥量,它仅在内核 space 中实现。在高层次上,它足以示范。

最初创建互斥量时,内核将内存区域与其相关联。该区域将保存被锁定或解锁的互斥量值。稍后,内核被要求锁定互斥量,它首先指示 CPU 发出内存屏障。互斥量必须充当屏障,以便在获得(或释放)互斥量后的所有内容 read/written 对其余 CPU 可见。然后,它使用 CPU 支持的比较和设置指令将内存区域值设置为 1,如果它被设置为 0。(有更复杂的可重入互斥锁,但我们不要用它们使图片复杂化)。 CPU 保证即使有多个线程同时尝试执行此操作,也只有一个会成功。如果操作成功,我们现在 'hold the mutex'。一旦要求内核释放互斥锁,内存区域将设置为 0(不需要有条件地这样做,因为我们知道我们持有互斥锁!)并发出另一个内存屏障。内核还会更新其表中的互斥锁状态 - 见下文。

如果互斥锁锁定失败,内核会将线程添加到它的表中,该表列出了等待释放特定互斥锁的线程。当互斥体被释放时,内核检查哪些线程正在等待这个互斥体,'schedules'(即准备执行)其中之一(如果有多个线程,将调度或唤醒哪个线程)取决于多种因素,在最简单的情况下,它只是随机的)。被调度的线程开始执行,再次锁定互斥体(此时可能再次失败!),生命周期继续。

希望它至少有一半意义:)