线程如何在禁用中断的情况下休眠?

时间:2013-02-23 22:04:04

标签: synchronization operating-system semaphore interrupt os161

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

我对这段代码的理解与讲义:
X:P()函数的流程
1.当线程调用P()时,我们禁用中断
2.检查我们是否有任何资源可用于sem->计数
 3.a)如果count为0,那么我们就去睡觉了  3.b)如果count!= 0,那么我们减少计数并允许调用线程继续到关键部分
4.启用中断
Y:V()函数的流程
1.当线程调用V()时,我们禁用中断
2.增加计数器,意味着现在有1个可用资源可供抓取 3.现在我们继续唤醒我们在P()中发送的所有线程,因为当线程试图抓住关键部分的锁时,没有足够的可用资源。 4.启用中断

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

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

为什么线程在禁用中断的情况下进入休眠状态?是不是很危险,因为它可能会错过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;
    }

谢谢。

1 个答案:

答案 0 :(得分: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()

至于信号量计数,我现在不愿意做更多的代码分析。你应该试着自己解决这个问题,除非其他人插入并覆盖它。