0

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

假设我们在 CPU 上有两个内核。我们有两个进程,每个核心上运行一个。我们现在在两个核心上调用 wait() ,因为我们希望进入临界区:

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

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

我怎么了?

感谢您的澄清。

4

3 回答 3

2

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

例如,在 Linux 上,您有:


您的概念是正确的,但是这两个操作(检查和 inc/dec)需要以“原子”方式进行 - 简单地说,这意味着它们作为一个无法拆分的操作发生(阅读Linearizability)。

此外,值得注意的是,您已经实现了一个“忙循环”,在使用操作系统时这是一个坏主意,因为您正在剥夺其他任务/进程的 CPU 时间并在不执行实际工作的情况下提高功耗 - 功能上面提到的将以0% 的 CPU 使用率“阻塞”,而如果有机会,您的将以 100% 的 CPU 使用率“阻塞


在单核上运行时尝试“玩”这些概念会更有运气(您可以将应用程序执行限制在单核上 - 查看sched_setaffinity()

但是,即使您成功了,您也几乎无法控制您的进程是否安排在错误的时间,从而导致您的示例应用程序以完全相同的方式中断。sched_setscheduler()通过调用with可能会进一步提高您正确操作的机会SCHED_FIFO,尽管我没有这方面的第一手经验(refref)。

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


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

于 2017-06-12T16:26:56.047 回答
1

使用信号量的内置函数可能会更好。要等待使用 pthreads 库,您将使用该pend()函数,并且要使信号量可用,您将使用该post()函数。

pend将等到 的值s大于 0。然后它会自动递减 的值,s然后继续前进。当然,具体如何实现取决于库。它可能看起来像这样:

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();
}

这段代码实际上是基于我为一个类实现的 RTOS。每个实现看起来都非常不同,还有很多我没有在这里展示,但它应该让你对它如何工作有一个基本的了解。

最后,您在假设的案例中提到有 2 个单独的进程共享一个信号量。这是可能的,您只需确保进行正确的函数调用以使信号量在进程之间可共享。

于 2017-06-12T16:48:09.333 回答
1

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

于 2017-06-12T16:08:29.507 回答