信号量在Multicore上等待()

时间:2017-06-12 12:39:09

标签: mutex semaphore multicore mutual-exclusion busy-waiting

我目前正在研究信号量和互斥是如何工作的,并遇到了以下问题。

假设我们在CPU上有两个核心。我们有两个进程,每个核心运行一个。我们现在要在两个核心上调用wait()调用,因为我们希望进入一个关键部分:

wait(){
  while(s.value <= 0)
    // busy waiting
  s.value--;
}

如果两个核心并行执行代码,并且初始信号量值为1,则两者都读取while循环语句,结果为false(自s = 1起)。这意味着,两者几乎同时递减信号量,结果为s = -1。现在,这两个流程同时进入其关键部分,这在互斥方面是不可能的。

我出错了什么?

感谢您的澄清。

3 个答案:

答案 0 :(得分:2)

正如您已经发现的那样,这些不是简单的用户空间函数 - 如果不使用内核提供的函数,您自己实现信号量或互斥量将会非常棘手(不可能?)。

例如,在Linux上你有:

你的概念是正确的,但是两个操作(检查和inc / dec)需要在&#34; atomic &#34;中进行。方式 - 简单地说这意味着它们作为一个无法拆分的操作发生(在Linearizability上阅读)。

此外,值得注意的是,您已经实施了一个“繁忙的循环”,当您使用操作系统是一个坏主意,因为您正在剥夺其他任务/流程来自CPU时间并在没有实际工作的情况下提高用电量 - 上面提到的功能将&#34; 阻止&#34; CPU使用率为0%,而你的将使用&#34; 阻止&#34;如果有机会,100%的CPU使用率。

你会有更多的运气试图“玩”。在单核上运行时有这样的概念(您可以将应用程序执行限制在单个核心 - 请查看sched_setaffinity()

然而,即使你实现这一目标,你几乎无法控制你的进程是否在一个糟糕的时间安排出来,导致你的示例应用程序以完全相同的方式中断。通过sched_setscheduler()致电SCHED_FIFO可能会进一步提高您正确操作的可能性,但我还没有第一手经验(ref,{{3} })。

无论哪种方式,这都不可能100%可靠,而内核支持的功能应该是。

如果您正在努力,那么在您自己的函数中使用实现细节的最佳方法是实现一个非常基本的循环调度程序(不会中断任务)并运行它在微观或单个线程中。

答案 1 :(得分:1)

在java和其他语言中,您可以使用synchronized来同步代码块或函数,并避免这种问题,因为当一个线程正在执行同步方法或块时,所有其他线程调用同步方法或阻塞(暂停执行)直到第一个线程完成。

答案 2 :(得分:1)

It's probably better to use the built-in functions for the semaphore. To wait using the pthreads library, you'd use the pend() function, and to make the semaphore available, you'd use the post() function.

pend will wait until the value of s is greater than 0. Then it will atomically decrement the value of s and then continue onward. How this is exactly implemented depends on the library, of course. It may look something like this:

sem_wait(){
    // Make a kernel function call to guarantee that the following code is atomic
    enter_critical_section();
    // Test the semaphore value. If it is positive, let the code continue.
    int sem_val = s.value;
    s.value--;
    if (sem_val > 0) {
        exit_critical_section();
        return;
    }
    else {
    // At this point we know the semaphore value is negative, so it's not available. We'd want to block the caller and make a context switch to a different thread or task.
    ... // Put the current thread on list of blocked threads
    ... // Make a context switch to a ready thread.
    }
    // When the semaphore is made available with a sem_post() function call somewhere else, there will eventually be a context switch back to this (blocked) thread. Simply exit the critical section, return back to the calling function, and let the program execute normally.
    exit_critical_section();
}

This code is actually based off an RTOS I implemented for a class. Each implementation will look very different, and there's a lot I haven't shown here, but it should give you a basic idea of how it could work.

Finally, you mentioned in your hypothetical case that there were 2 separate processes sharing a single semaphore. That's possible, you just have to make certain to make the right function calls to make the semaphore shareable between processes.