【问题标题】:Implement semaphore in User Level C在用户级别 C 中实现信号量
【发布时间】:2016-01-26 11:24:26
【问题描述】:

信号量的有效且必要的实现要求它是原子指令。

我在互联网上看到几个用户级 C 实现使用计数等变量或队列等数据结构实现信号量。但是,涉及变量的指令不会作为原子指令运行。那么任何人如何在用户级别 C 中实现信号量。

一个c库semaphore.h是如何实现信号量的?

【问题讨论】:

    标签: c operating-system semaphore atomic locks


    【解决方案1】:

    答案几乎肯定是“它没有”——相反,它会调用提供必要原子操作的内核服务。

    【讨论】:

    • 不一定,尽管对于具有不同地址空间的进程间共享资源来说,这将是最简单的解决方案。在用户空间中实现同步原语仍然有利于提高性能。
    • 只有在平均线程等待时间小于通过避免系统调用节省的时间时才会有利于性能;用户空间代码无法从等待线程中移除执行。
    • @MartinJames 当然,这仅适用于用户级线程,我应该添加这个。对于“普通”内核级线程,您可以考虑真正需要一个信号量,并可能使用一些无锁实现(再次使用原子),但这不是问题的范围。
    【解决方案2】:

    直到标准 C 中是不可能的。正如您所说,您需要的是 atomic 操作。 最终指定它们,例如参见 stdatomic.h

    如果您使用的是旧版本的标准,则必须直接使用嵌入式汇编程序或依赖编译器的供应商特定扩展,例如参见GCC atomic builtins。当然,处理器支持内存屏障、检查和交换操作等指令。它们只是不能从纯 和更早的版本访问,因为并行执行不在标准范围内。

    阅读 MartinJames 的评论后,我应该在此处添加说明:这仅适用于您在用户空间中实现所有线程的情况,因为信号量必须阻塞等待它的线程,因此如果线程由内核的调度程序管理(如例如 Linux 上的 pthreads 的情况),有必要进行系统调用。不在您的问题范围内,但原子操作可能仍然对于实现例如有趣无锁数据结构。

    【讨论】:

      【解决方案3】:

      您可以像这样简单地实现信号量操作:

      void sema_post(atomic_uint *value) {
          unsigned old = 0;
          while (!atomic_compare_exchange_weak(value, &old, old + 1));
      }
      
      void sema_wait(atomic_uint *value) {
          unsigned old = 1;
          while (old == 0 || !atomic_compare_exchange_weak(value, &old, old - 1));
      }
      

      语义上没问题,但它确实在sema_wait 中忙于等待(旋转)。 (注意sema_post 是无锁的,尽管它也可能旋转。)相反,它应该休眠直到value 变为正数。这个问题不能用原子来解决,因为所有的原子操作都是非阻塞的。在这里,您需要操作系统内核的帮助。因此,高效的信号量可以使用基于原子的类似算法,但在两种情况下进入内核(有关此方法的更多详细信息,请参阅 Linux futex):

      • sema_wait:当它找到value == 0时,请求睡眠
      • sema_post:当 value 从 0 增加到 1 时,请求唤醒另一个睡眠线程(如果有)

      一般来说,要在数据结构上实现无锁(使用原子)操作,要求每个操作都适用于任何状态。对于信号量,wait 不适用于值 0。

      【讨论】:

        猜你喜欢
        • 2016-07-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-08-25
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多