sem_init()的手册页说“初始化已经初始化的信号量会导致未定义的行为”。为什么会这样,Linux上到底会发生什么?
这对我来说没有意义,因为当你第一次调用sem_init()时,(未初始化的)sem_t可能具有确切的内容作为初始化的sem_t - 如果手册是正确的,那么sem_init()只是不起作用。
答案 0 :(得分:3)
在Linux上,在没有任何系统资源的情况下实现信号量,sem_init
只填充sem_t
结构成员,因此如果不止一次调用它,就不会发生任何不良事件。然而,总的来说情况会更糟糕。
如果sem_t
只是一个包含指向已分配对象的指针的虚拟对象(注意:这对于进程共享信号量不起作用),则会多次调用sem_init
来泄漏内存
同样,如果sem_t
只包含对内核管理资源的引用(如文件描述符编号),则会多次调用sem_init
来泄漏这些内核资源。
更糟糕的是,如果库实现使用sem_t
对象内的prev / next指针维护所有实例化信号量的链表(对于进程共享情况也是如此),那么通过调用{ {1}} sem_init
已经是列表中的一部分。
POSIX信号量的标准允许支持不同类型系统上的实现所需的各种实现类型(例如,没有原子比较和交换指令的机器,没有内核的裸机,......)所以它保留了未定义的行为,以免强加可能限制实现选择的要求。
答案 1 :(得分:2)
为什么会这样
从API设计师的角度考虑它。信号量可以被视为一个被创建,使用并最终被处置的抽象对象。
现在的任务是将其映射到C(或任何其他语言)。信号量实现需要获取资源,可能是操作系统维护的资源。以上的实时循环很有意义。
最终确定了API,并进行了第一次实施。许多角落案件或额外要求很快就会出现。例如,假设当前实现使得允许它变得微不足道,是否可以多次调用sem_init
。另一个(可能)是应该可以选择是否在线程或进程之间共享信号量。
在每种情况下,API设计师都必须权衡利弊:
在这种情况下,似乎允许双重初始化将通过大多数这些标准获得否。所以决定不允许它。它可能仍适用于您的特定实现,编译器,系统甚至大多数实现,编译器,系统。
如何表达?好吧,你在手册中称之为未定义的行为,并且每个人都知道不要这样做。对环境有良好工作直觉的人很容易猜测行为可能是什么。但是,只有傻瓜会依赖它。
(未初始化的)sem_t可以具有确切的内容作为初始化的sem_t
这是事实。但是,我们假设sem_t
拥有指向sem_init
使用malloc
分配的一堆堆内存的指针。随机非初始化sem_t
完全有可能具有完全相同的指针值,但它对应的资源将不存在。