【问题标题】:Scheduling of Process(s) waiting for Semaphore等待信号量的进程调度
【发布时间】:2012-02-03 14:48:49
【问题描述】:

总是说当信号量的计数为0时,请求信号量的进程被阻塞并被添加到等待队列中。
当某个进程释放信号量并且计数从 0->1 增加时,一个阻塞进程被激活。这可以是任何进程,从被阻塞的进程中随机挑选出来。

现在我的问题是:
如果将它们添加到队列中,为什么阻塞进程的激活不是按 FIFO 顺序进行的?我认为从队列中选择下一个进程而不是随机选择一个进程并授予它信号量会很容易。如果这个随机逻辑背后有什么想法,请解释一下。另外,内核如何从队列中随机选择一个进程? 就队列数据结构而言,从队列中获取随机进程也是一件复杂的事情
标签:各种操作系统,因为每个操作系统都有一个通常用 C++ 编写的 内核,而 mutex 具有相似的概念

【问题讨论】:

  • 您似乎在交替使用“Q”和“Queue”。也许这就是你困惑的根源。

标签: c++ operating-system kernel mutex semaphore


【解决方案1】:

这里的所有其他答案都是对基本问题的很好描述——尤其是在线程优先级和就绪队列方面。然而,要考虑的另一件事是 IO。我在这里只谈论 Windows,因为它是我所知道的唯一具有任何权限的平台,但其他内核可能有类似的问题。

在 Windows 上,当 IO 完成时,称为内核模式 APC(异步过程调用)的东西会排队等待启动 IO 的线程以完成它。如果线程碰巧正在等待调度程序对象(例如您的示例中的信号量),则线程将从该对象的等待队列中删除,这会导致(内部内核模式)等待(类似于)STATUS_ALERTED 完成。现在,由于这些内核模式 APC 是一个实现细节,并且您无法从用户模式看到它们,WaitForMultipleObjects 的内核实现会在此时重新启动等待,这会导致您的线程被推到队列的后面。从内核模式的角度来看,队列仍然是 FIFO 顺序,因为底层等待 API 的第一个调用者仍然在队列的头部,但是从你的角度来看,在用户模式下,你只是被推到由于您没有看到并且很可能无法控制的事情而排在队列的后面。这使得队列顺序在用户模式下显得随机。实现仍然是一个简单的 FIFO,但由于 IO,它看起来不像是更高抽象级别的实现。

我在这里猜测得更多,但我认为类 unix 的操作系统在信号传递和内核需要劫持进程以在其上下文中运行的地方有类似的限制。

现在这并不总是发生,但文档必须是保守的,除非明确保证订单是 FIFO(如上所述 - 至少对于 windows - 它不可能)然后描述了排序在文档中作为“随机”或“无证”或其他东西,因为随机过程控制它。它还使操作系统供应商可以在以后更改顺序。

【讨论】:

    【解决方案2】:

    当一个进程被“随机”调度时,并不是一个进程是随机选择的;就是选择过程是不可预测的。

    Windows 内核使用的算法是有一个线程队列(Windows 调度“线程”,而不是“进程”)等待信号量。当信号量被释放时,内核调度队列中等待的下一个线程。但是,调度线程并不会立即使该线程开始执行;它只是通过将线程放入等待运行的线程队列中来使线程能够执行。在 CPU 没有更高优先级的线程可以执行之前,该线程不会真正运行。

    当线程在调度队列中等待时,另一个实际正在执行的线程可能在同一个信号量上等待。在传统的队列系统中,新线程必须停止执行并进入队列的末尾,排队等候该信号量。

    然而,在最近的 Windows 内核中,新线程不必停止并等待该信号量。如果已分配该信号量的线程仍在运行队列中,则该信号量可能会重新分配给旧线程,导致旧线程再次返回等待该信号量。

    这样做的好处是,本来要在队列中等待信号量,然后在队列中等待运行的线程根本不必等待。缺点是您无法预测接下来哪个线程会真正获得信号量,而且这是不公平的,因此等待信号量的线程可能会饿死。

    【讨论】:

      【解决方案3】:

      FIFO 是系统中最简单的等待列表数据结构 这不支持优先级,但这不是绝对的答案 否则。根据所选择的调度算法,不同的 线程可能有不同的绝对优先级,或者某种 衰减优先级可能生效,在这种情况下,操作系统可能会选择 在之前的某个时间间隔内具有最少 CPU 时间的线程。 由于此类策略被广泛使用(尤其是后者),因此 通常的规则是认为你不知道(尽管绝对 优先级,它将是具有最高优先级的线程之一)。

      【讨论】:

        【解决方案4】:

        进程调度算法非常特定于系统功能和操作系统设计。这个问题很难给出一个好的答案。如果我使用的是普通 PC,我想要具有良好吞吐量和平均等待/响应时间的东西。如果我在一个系统上,我知道我所有作业的优先级并且知道我绝对希望我的所有高优先级作业首先运行(并且不关心抢占/饥饿),那么我需要一个优先级算法。

        就随机选择而言,动机可能有多种原因。一种是如上所述的良好吞吐量等的尝试。但是,这将是不确定的(假设的)并且无法证明。该属性可能是对概率的利用(随机样本等),但同样,证明只能基于关于这是否真的有效的经验数据。

        【讨论】:

        • 机器中所做的一切都必须是伪随机的。就像在一个简单的程序srand(time(NULL)) 中一样,算法用于根据当前时间生成一个数字。您可以使用像这样简单的东西(即线程安全的 rand() 产生数字 0 - num_threads_in_queue)或开发另一种算法。
        【解决方案5】:

        不是不能先进先出;事实上,我敢打赌,很多实现都是,只是出于你所说的原因。规范不是随机选择该过程;就是它没有被指定,所以你的程序不应该依赖于它以任何特定的方式被选择。 (它可以随机选择;仅仅因为它不是最快的方法并不意味着它不能完成。)

        【讨论】:

        • 这就是我关心的问题。如果有人编写内核,进行随机拾取,那么他的思想和策略,数据结构等背后的动机是什么。不确定信号量,但是是的,在互斥锁的情况下,线程随机唤醒。我猜他们俩都会分享类似的逻辑。所以我缺少一些东西。这让我觉得有些进程会继续检查,然后如果它发现信号量 val>0,它会得到它,而其他进程将被阻塞,这与理论所说的正好相反,我在这里寻求极客的帮助8-)
        • 并不是有人写了一个信号量,它使用 rand() 来挑选线程释放。那将是疯狂的。就是发生了一堆你看不到的事情,这使它看起来很随机。例如,假设线程按 FIFO 顺序释放,但时间片在 ret 返回到您的代码之前结束,并且另一组线程在您重新安排之前运行。这使它看起来很随机,即使内核仍在使用 FIFO。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-01-13
        • 1970-01-01
        • 2014-10-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多