【问题标题】:How can semaphores implemented on multi-core systems?信号量如何在多核系统上实现?
【发布时间】:2016-07-15 18:46:56
【问题描述】:

我无法理解 Posix 如何允许任何线程在信号量上解锁(发布)。让我们考虑以下示例:

// sem1 and sem2 are a Posix semaphore,
// properly initialized for single process use
// at this point, sem2 is locked, sem1 is unlocked 
// x and y are global (non-atomic, non-volatile) integer variables
// thread 1 - is executing right now

rc = sem_wait(&sem1); // succeeded, semaphore is 0 now
x = 42;
y = 142;
sem_post(&sem2);
while (true);

// thread 2. waits for sem2 to be unlocked by thread1 
sem_wait(&sem2);
sem_post(&sem1);

// thread 3
sem_wait(&sem1); // wakes up when sem1 is unlocked by thread2
#ifdef __cplusplus
std::cout << "x: " << x << ; y: " << y << "\n";
#else
printf("x: %d; y: %d\n", x, y);
#endif

现在,根据我所读到的所有内容,这段代码 100% 符合逾越节的犹太教规。在线程 3 中,我们保证看到 x 为 42,y 为 142。我们免受任何种族的影响。

但这是我无法理解的。所有这些线程都可能在 3 个不同的内核上执行。如果芯片内部没有强大的内存排序(ARM、PowerPC)或写入不是原子的(x86 用于未对齐的数据),Core2 上的线程 2 如何可能请求 Core1(忙于线程 1)正确释放数据/完成写入/ 等等?据我所知,没有这样的命令!

我在这里缺少什么?

编辑。请注意,建议的重复并不能回答我的问题。它重申了我的说法,但没有解释如何可能达到这种效果。特别是,它没有解释 Core2 如何在 Core1 的缓存中设置内存屏障。

【问题讨论】:

  • 我分享你的困惑。对我来说,这似乎是一个潜在的竞争条件。您阅读的哪一部分表明这是安全的?
  • 阅读:pubs.opengroup.org/onlinepubs/9699919799/basedefs/…,其中声明 以下函数相对于其他线程同步内存
  • @flatmouse,无处不在? man sem_overview 例如。
  • @SergeyA 你的链接都没有回答我的问题,它只是重申了我所说的。我在问怎么做。我花了大约 30 秒才找到这个问题的副本。不,你没有问如何,你问的是why:这个问题的标题是Why any thread can unlock a semaphore? Why 是因为 sem_wait() 和 sem_post() 根据我链接到的 POSIX 标准同步内存。您还问我在这里缺少什么?您错过了 POSIX 需要与 sem_post() 和 sem_wait() 进行内存同步这一事实。

标签: c++ multithreading posix semaphore


【解决方案1】:

...因为它是这样定义的。

任何线程都可以将一个单元发布到信号量。

信号量主要不是锁定机制,因此“解锁”是不合适的。

信号量支持发布和等待。

在复杂架构的情况下,例如。许多多核处理器、信号量的实现以及其他线程间同步机制可能会变得相当复杂。例如,可能需要通过处理器间驱动程序发送同步消息,因此硬件中断其他内核以强制它们处理同步。

【讨论】:

  • 仍然没有答案。我对这到底是如何实现的有一个非常具体的问题。
  • 你有什么改进标题的建议吗?我自己对此不太满意,但我想不出更好的。
  • 好吧,现在我们正在取得进展。我的问题是我无法找到任何迹象表明此“通过处理器间驱动程序的同步消息”存在。
  • 如何在多核系统上实现信号量?
  • @SergeyA - 您认为如何跨多个内核实现通信?例如,当一个进程终止时,必须先停止其所有线程,然后才能释放内存等其他资源。如果进程线程在请求​​终止的内核之外的另一个内核上运行,则必须停止它,因此另一个内核必须被硬件中断。这意味着一个内核间驱动程序。
【解决方案2】:

看起来它是通过等待来自 Win32 的 POSIX 线程库的信号(由操作系统控制)来实现的:

       * If the sema is posted between us being cancelled and us locking
       * the sema again above then we need to consume that post but cancel
       * anyway. If we don't get the semaphore we indicate that we're no
       * longer waiting.
       */
      if (*((sem_t *)sem) != NULL && !(WaitForSingleObject(s->sem, 0) == WAIT_OBJECT_0))
    {
      ++s->value;
#if defined(NEED_SEM)
      if (s->value > 0)
        {
          s->leftToUnblock = 0;
        }
#else
      /*
       * Don't release the W32 sema, it doesn't need adjustment
       * because it doesn't record the number of waiters.
       */
#endif /* NEED_SEM */
    }
      (void) pthread_mutex_unlock (&s->lock);
    }
}

当然,我们没有可供我们使用的 Windows 源代码。所以 POSIX 实现的路径到此结束。

Linux 并非如此。 sem_wait.c 使用futex_wait(),并在此函数的源代码中确定CPU 是否可以支持某些函数。例如; CPU有比较和交换功能吗?但即使在那里,也没有充分考虑架构的全部功能。 lll_futex_wait() 等函数在lowlevellock.h 中定义。所以对于 PowerPC,我们有powerpc/lowlevellock.h,例如下面的 sn-p:

/* Set *futex to ID if it is 0, atomically.  Returns the old value */
#define __lll_robust_trylock(futex, id) \
  ({ int __val;                                   \
     __asm __volatile ("1:  lwarx   %0,0,%2" MUTEX_HINT_ACQ "\n"          \
               "    cmpwi   0,%0,0\n"                 \
               "    bne 2f\n"                     \
               "    stwcx.  %3,0,%2\n"                \
               "    bne-    1b\n"                     \
               "2:  " __lll_acq_instr                 \
               : "=&r" (__val), "=m" (*futex)                 \
               : "r" (futex), "r" (id), "m" (*futex)              \
               : "cr0", "memory");                    \
     __val;                                   \
  })

所以答案可能是,如果它是在 Linux 上实现的,那么架构特定库中可能有一些实现或解决方法可以解决“缺失”指令。

【讨论】:

  • 不确定此代码的相关性。所有我看到的都是特定于 windows 的 WaitForSingleObject 和 pthread_mutex_unlock,这不可能从非拥有线程发生。
  • 正确,这是来自 Win32 的 POSIX 线程库。在 Linux 中,sem_wait 函数的实现方式不同,但提供相同的功能。问的关于 POSIX 的问题,没有指定操作系统。
  • 确实,。实际的信号量实现必然是特定于操作系统和体系结构的。 POSIX 生活在此之上。
【解决方案3】:

如果 sem1 = 1 和 sem2 = 0 的初始值,那么线程 3 和线程 1 都可以锁定。这取决于哪个线程先进入。假设如果线程3先执行,那么sem1现在将变为0,线程1无法获得锁,线程2也无法获得锁,因为它依赖于线程1。如果初始值sem1 = 0 那么没有线程可以得到锁....我认为这会有所帮助..

【讨论】:

  • 这根本不是答案,抱歉。
猜你喜欢
  • 2017-05-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-03
相关资源
最近更新 更多