操作系统实现事件/信号/等待句柄

4

我很好奇操作系统如何实现唤醒等待事件/句柄的线程。

例如,假设一个操作系统线程不断扫描等待句柄列表,并在必要时执行相应的线程。我认为这种实现方式不太可能,因为它似乎效率低下。

我认为更可能的是,操作系统会在包含与公开等待句柄/事件关联的同步原语的一段内存上设置硬件中断,然后当它们被触发时,可以谨慎地安排线程调度,以确保不会多次调度。

编辑

实际上,我想更具体地思考的是:是什么唤醒了正在睡眠的核心以运行阻塞的线程?

3个回答

3
为了深入理解这个问题,您需要参加操作系统课程(或至少购买一本有关该主题的好书),因为它实际上涉及到许多系统。
但基本上,它与线程状态管理有关。一个线程在任何时候都处于几种不同的状态之一:睡眠、就绪或运行(通常还有更多,但对于本次讨论的目的,只需要这些)。处于运行状态的线程实际上正在运行,并且线程中的代码正在执行。处于“睡眠”状态的线程未运行,调度程序在决定下一个要运行的线程时将跳过它。处于“就绪”状态的线程当前未运行,但是一旦另一个线程进入睡眠状态或其时间片用尽,调度程序可以自由选择将该线程调度为进入运行状态。
因此,当您在互斥对象上调用“等待”时,操作系统会检查对象是否已由另一个线程拥有,并且如果是,则将当前线程的状态设置为“睡眠”,并且还将线程标记为“等待”该特定互斥对象。
当拥有互斥对象的线程完成时,操作系统会循环遍历所有等待它的线程,并将它们设置为“就绪”。下一次调度程序到达时,它会看到一个“就绪”线程,并将其置于“运行”状态。线程开始运行并检查是否可以再次锁定互斥对象。这一次没有人拥有它,因此它可以继续愉快地进行。
实际上,它比这复杂得多,需要花费大量的精力使系统尽可能高效(例如,避免唤醒一个线程,只是让它立即进入睡眠状态,避免在一个具有许多其他线程等待的互斥对象上饿死一个线程等等)。

实际上,我想更具体地思考的是,当阻塞线程被唤醒时,会发生什么来唤醒休眠的核心? - iam
@wb:调度程序在特定时间运行(每个时间片段为 10-20 毫秒,当运行线程进入睡眠模式等),并循环遍历每个线程以查看哪些线程处于“就绪”状态,并将具有最高优先级的线程标记为“运行”。当然,它不会通过 O(n) 循环遍历 每个 线程,它(可能)将它们保留在某种优先队列中。 - Dean Harding

1

入门教材的答案是,当一个线程等待事件发生而睡眠时,它会被放入等待线程队列中。线程被标记为“等待”,因此操作系统的进程调度程序在寻找要在处理器上运行的内容时跳过该线程。最终(在正确的程序中),另一个线程将唤醒等待事件队列中的一个或所有线程。然后这些线程被标记为“就绪”,操作系统再次开始调度它们。

当然,实际实现起来相当棘手。我想这才是你真正的问题。对于Linux,你寻找的机制称为futex,它们对我来说过于复杂了,我无法在此做出公正评价。如果维基百科中的简介引起了您的兴趣,请深入研究该词条底部的外部链接。


你提到“最终(在正确的程序中),另一个线程将唤醒等待事件队列的一个或所有线程。”,这个其他线程是什么?它存在于内核中吗? - CMCDragonkai
@CMCDragonkai:完整的答案取决于您的程序使用的编程语言以及线程所阻塞的事件队列类型。对于使用像C这样简单语言编写并等待本地互斥锁的程序,该程序应该已经创建了另一个线程,并且该线程将存在于内核中。 - Karmastan

0

我认为实现更简单,一个线程被放在等待线程列表中(所有正在等待某个事件/句柄/互斥锁的线程)。当同步原语唤醒时,所有线程都将转移到运行状态并清除该列表。


所以基本上,每当我们有一个被阻塞/等待的进程时,在操作系统中,线程只是被添加到一个列表中,当它正在等待的事物以某种方式发生变化时,该列表将被触发? - CMCDragonkai

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接