如果调用sem_init()两次会发生什么?

4

sem_init() 的 man 手册中说:"对已经初始化的信号量进行初始化将导致未定义的行为。" 为什么会这样,Linux 中会发生什么?

我觉得这没什么道理,因为当您第一次调用 sem_init() 时,未初始化的 sem_t 可能与已初始化的 sem_t 具有完全相同的内容 -- 如果手册是正确的,则 sem_init() 简单地不起作用。


试一试,亲自体验。 - TerryG
我尝试过,似乎工作正常,但是手册让我感到有些紧张,因为我不想在某些边际情况下措手不及。 - zhao
@ZhaoWu:无论如何你都不应该这样做。如果你发现自己需要这样做,那么你的程序中存在逻辑错误。 - R.. GitHub STOP HELPING ICE
当然。我只是出于好奇想尝试一下。 - zhao
2个回答

3
在Linux上,信号量是在没有任何系统资源的情况下实现的,sem_init只是填充了sem_t结构成员,因此如果多次调用它,不会发生任何不良反应。然而,一般情况下可能会发生更糟糕的事情。
如果sem_t只是一个包含指向已分配对象的指针的虚拟对象(注意:这对于进程共享的信号量是行不通的),则通过多次调用sem_init将泄漏内存。
同样地,如果sem_t只是包含对内核管理资源的引用(如文件描述符号码),则通过多次调用sem_init将泄漏这些内核资源。
更糟糕的是,如果库实现在sem_t对象内部使用prev/next指针维护所有实例化信号量的链接列表(这也不可能适用于进程共享的情况),则通过在已经是列表一部分的sem_t上调用sem_init将破坏这个列表。
POSIX信号量标准允许各种实现类型,这可能是为了支持不同类型系统上的实现(例如没有原子比较和交换指令的机器,没有内核的裸机,...)。因此,它将行为定义为未定义状态,以不强制施加可能限制实现选择的要求。

谢谢回答。现在我完全明白了。 - zhao
sem_init()函数中pshared参数的相关问题。如果一个信号量将要在进程之间共享,它必须驻留在共享内存中。我不认为sem_init()会执行任何shm_open/shmget/mmap操作,那么这个参数在Linux上有用吗? - zhao
为了让pshared参数有用,您传递给sem_init的sem_t地址需要驻留在共享内存中。您负责提供此共享内存。它可以通过mmap和MAP_ANONYMOUS创建,并在fork时继承(没有其他方法来共享这样的内存),或者在sysv或POSIX共享内存段(shmget/shmat/等)或文件的共享映射中创建。 - R.. GitHub STOP HELPING ICE

2
从API设计师的角度考虑,信号量可以被看作是一个抽象对象,它被创建、使用和最终处理。
现在的任务是将其映射到C(或任何其他语言)。信号量实现将需要获取资源,可能是由操作系统维护的资源。上述生命周期有很多意义。
API已经确定,第一次实现已经完成。很快就会出现许多边角情况或额外要求。例如,是否可以多次调用sem_init,鉴于当前实现使其变得微不足道。另一个(也许)是应该能够选择信号量是在线程之间共享还是进程之间共享。
在每种情况下,API设计者都必须权衡利弊:
- 对API的实现者来说,这是否是额外的负担? - 它是否可以通过其他方式实现?(即它是否必须在库/系统级别上实现) - 它是否分散了API的核心功能? - 它是否被认为是所选语言中的惯用法? - 它是否被认为是API的良好模式? - 它易错吗/是否会导致安全风险? - ...
在这种情况下,似乎允许双重初始化将根据大多数标准获得“否定”答案。因此,决定不允许它。它可能仍然适用于您特定的实现、编译器、系统或甚至大多数实现、编译器、系统。
如何传达?嗯,你在手册中称其为“未定义行为”,每个人都知道不要这样做。对环境有很好工作直觉的人可以轻松地猜测行为可能是什么。但是只有傻瓜才会依赖它。
那是真的。但是,假设sem_t保存指向由malloc分配的堆内存块的指针。一个随机未初始化的sem_t可能具有完全相同的指针值,但它对应的资源将不存在。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接