在用户级C中实现信号量

在用户级C中实现信号量,c,operating-system,semaphore,atomic,locks,c11,c99,C,Operating System,Semaphore,Atomic,Locks,C11,C99,信号量的有效和必要实现要求它是原子指令 我在internet上看到几个用户级C实现,它们使用变量(如count)或数据结构(如queue)实现信号量。但是,涉及变量的指令不能作为原子指令运行。那么,如何在用户级C中实现sempahore呢 c库信号量.h是如何实现信号量的?答案几乎肯定是“它不会”-相反,它会调用提供必要原子操作的内核服务。答案几乎肯定是“它不会”-相反,它将调用提供必要原子操作的内核服务。在标准C中,直到。正如你所说,你需要的是原子操作。最后指定它们,请参见示例 如果您使用的是

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

我在internet上看到几个用户级C实现,它们使用变量(如count)或数据结构(如queue)实现信号量。但是,涉及变量的指令不能作为原子指令运行。那么,如何在用户级C中实现sempahore呢


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

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

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

在标准C中,直到。正如你所说,你需要的是原子操作。最后指定它们,请参见示例

如果您使用的是较旧版本的标准,则必须直接使用嵌入式汇编程序,或者依赖编译器的特定于供应商的扩展,请参见示例。当然,处理器支持内存屏障、检查和交换操作等指令。由于并行执行不在标准范围内,因此无法从pure和更早版本访问这些指令

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

在之前,在标准C中是不可能的。正如你所说,你需要的是原子操作。最后指定它们,请参见示例

如果您使用的是较旧版本的标准,则必须直接使用嵌入式汇编程序,或者依赖编译器的特定于供应商的扩展,请参见示例。当然,处理器支持内存屏障、检查和交换操作等指令。由于并行执行不在标准范围内,因此无法从pure和更早版本访问这些指令


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

您可以实现如下简单的信号量操作:

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\u wait
中它确实在忙着等待(旋转)。(请注意,
sema_post
是无锁的,尽管它也可能会旋转。)相反,它应该休眠,直到
value
变为正值。原子学无法解决这个问题,因为所有的原子操作都是非阻塞的。这里您需要OS内核的帮助。因此,有效的信号量可以使用基于原子的类似算法,但在两种情况下进入内核(有关此方法的更多详细信息,请参阅Linux futex):

  • sema\u wait
    :当它找到
    value==0
    时,请求睡眠
  • sema\u post
    :当它将
    值从0增加到1时,要求唤醒另一个睡眠线程(如果有)

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

您可以实现如下简单的信号量操作:

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\u wait
中它确实在忙着等待(旋转)。(请注意,
sema_post
是无锁的,尽管它也可能会旋转。)相反,它应该休眠,直到
value
变为正值。原子学无法解决这个问题,因为所有的原子操作都是非阻塞的。这里您需要OS内核的帮助。因此,有效的信号量可以使用基于原子的类似算法,但在两种情况下进入内核(有关此方法的更多详细信息,请参阅Linux futex):

  • sema\u wait
    :当它找到
    value==0
    时,请求睡眠
  • sema\u post
    :当它将
    值从0增加到1时,要求唤醒另一个睡眠线程(如果有)

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

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