2

我试图了解下面的代码是如何工作的。这直接来自我的教授讲座幻灯片。这个 P() 和 V() 函数是我们在类 (OS161) 中使用的操作系统中信号量实现的一部分。我认为您可能需要了解 OS161 才能回答我的问题,因为它被广泛使用,希望有人可以回答这个问题。

我通过讲义对这段代码的理解:
X:P() 函数的流程
1. 当线程调用 P() 时,我们禁用中断
2. 检查 sem->count 上是否有可用资源
3.a) 如果count 为 0 然后我们进入睡眠
3.b) 如果 count != 0 然后我们递减 count 并允许调用线程继续到临界区
4. 启用 V() 函数的中断
Y:Flow
1. 当一个线程调用 V() 时,我们禁用中断
2. 增加计数器,这意味着现在还有 1 个资源可用于抓取
3. 现在我们继续唤醒我们发送到睡眠的所有线程 P( ),因为在线程试图抓住关键部分的锁时没有足够的可用资源
4. 启用中断

我的问题:
1. “禁用中断”部分是否禁用特定线程上的中断,还是禁用所有中断?
2.在V()函数上,当我们唤醒所有线程时,P()函数中休眠在while循环内的线程开始执行while循环。在讲座中,它说一个线程抓住锁并休息回去睡觉。我的问题是为什么“sem->count == 0”条件不会对其他线程评估为假,而对其他线程只有一个。

我真的很想知道中断禁用部分是如何工作的。这是我的第一个问题。它会停止线程调度程序吗?它会停止系统中的上下文切换吗?

为什么线程在中断禁用的情况下进入睡眠状态?是不是很危险,因为它可能会错过 I/O 完成信号和其他东西?

    P(sem) {
        Disable interrupts;
        while (sem->count == 0) {
        thread_sleep(sem); /* current thread
                               will sleep on this sem */
        }
        sem->count--;
        Enable interrupts;
    }
    V(sem) {
        Disable interrupts;
        sem->count++;
        thread_wakeup (sem); /* this will wake
        up all the threads waiting on this
                   sem. Why wake up all threads? */
        Enable interrupts;
    }

谢谢你。

4

1 回答 1

5

CPU对线程一无所知,它们只是在软件中实现的逻辑/抽象概念。但是 CPU 确实知道中断,它们是真实存在的,每当有来自某个设备的中断时,CPU 就会停止执行它一直在执行的任何操作,并开始执行专用于处理此特定中断的例程。一旦完成,例程发出中断处理完成的信号,CPU 恢复执行中断处理例程抢占的任何内容。

如果被抢占的代码属于一个线程,那就这样吧。如果它是另一个中断处理例程,也可以。

就在中断处理例程开始之前,CPU 将一些执行上下文(一些通用寄存器,可能还有一些控制/系统寄存器)保存在堆栈或其他地方,因此例程可以将它们用于自己的目的,然后在程序结束时,CPU 从它们存储的地方恢复这些寄存器,就好像从被中断代码的角度来看没有发生任何事情一样。如果例程更改了这些寄存器,CPU 将在其他地方恢复执行,而不是在中断前最后一次执行的地方。

因此,在那里,您可以使用中断在各种代码、线程或您拥有的东西之间切换执行。事实上,这正是调度器工作的数量。它们从定时器接收周期性中断,并在中断处理程序中将抢占代码(例如线程 A)的上下文保存在内存中,并从内存中加载另一个抢占代码(例如线程 B)的上下文并返回,从而继续在另一个线程中执行.

如果您禁用这些计时器中断,则周期性线程调度/切换也将被禁用。中断会影响整个 CPU 和当前正在执行的线程(或其他任何线程),并且通过归纳它们会影响所有线程。

知道了?

现在,如果系统中有线程,那么总是至少有一个线程可以执行。那是因为 CPU 需要执行某些事情,它不能只是停下来等待线程从无处到达(毕竟,是 CPU 创建线程并使它们可运行并运行它们)。为此目的,系统中有一个虚拟(或不那么虚拟)线程,它具有低优先级并且几乎什么都不做,永远循环并可能告诉 CPU 它可能会切换到较低的功耗状态或停止直到中断到来. 中断将结束低功耗模式并导致代码继续执行。

因此,当线程阻塞信号量或其他同步原语时,调度程序只需选择另一个线程来执行。如果所有线程都被阻塞,则选择虚拟线程。

在您的代码中,中断会在短时间内被禁用,而内核代码正在操作各种全局变量(例如,阻塞/休眠和就绪线程的列表)。这很正常。你不想要这里的竞争条件。当调度程序选择另一个线程来执行并继续执行它时,中断被重新启用。

请注意,当某个当前线程完成休眠时(例如,当某个其他线程唤醒它时),它总是启用中断:

spl = splhigh(); // disable interrupts
while (sem->count==0) {
  thread_sleep(sem); // context switch (to another thread and then back) occurs here
}
sem->count--;
splx(spl); // <-- re-enable interrupts here

以这种方式阻塞的每个线程都会在它被唤醒并被调度程序选择运行时再次启用中断。

考虑一下。您在 2 个(或更多)线程中有 2 个(或更多)上述或类似代码的实例。当一个线程进入thread_sleep()或类似的函数时,其他一些线程会从它的thread_sleep()或类似的函数中出来并重新启用中断。

遵循这条路径的代码和注释:

P()
  thread_sleep()
    mi_switch()
      md_switch()
        mips_switch()

至于信号量,我现在不愿意做更多的代码分析。您应该尝试自己弄清楚,除非其他人插话并掩盖它。

于 2013-02-24T03:35:06.733 回答