【问题标题】:Semaphores wait() on Multicore多核上的信号量 wait()
【发布时间】:2017-06-12 12:39:09
【问题描述】:

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

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

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

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

我做错了什么?

感谢您的澄清。

【问题讨论】:

  • 等待非零计数和递减通常在操作系统内核中处理,其方式使检查和递减成为原子操作。

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


【解决方案1】:

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

例如,在 Linux 上,您有:


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

此外,值得注意的是,您已经实现了一个“忙循环”,这在使用操作系统时是一个坏主意,因为您正在剥夺其他任务/进程的 CPU 时间并提高在没有实际工作的情况下使用电源 - 上面提到的函数将“block”以 0% 的 CPU 使用率,而你的将“block”以 100% 的 CPU 使用率,如果给定机会。


在单核上运行时尝试“玩”这些概念会更幸运(您可以将应用程序的执行限制在单核上 - 请参阅 sched_setaffinity()。

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

无论如何,这不可能是 100% 可靠的,而内核支持的函数应该是。


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

【讨论】:

    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      最好使用信号量的内置函数。要使用 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 个单独的进程共享一个信号量。这是可能的,您只需确保进行正确的函数调用以使信号量在进程之间可共享。

      【讨论】:

        猜你喜欢
        • 2016-07-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-08-18
        • 2017-07-02
        • 2011-06-30
        • 1970-01-01
        相关资源
        最近更新 更多