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

时间:2014-08-29 05:41:36

标签: c linux semaphore

sem_init()的手册页说“初始化已经初始化的信号量会导致未定义的行为”。为什么会这样,Linux上到底会发生什么?

这对我来说没有意义,因为当你第一次调用sem_init()时,(未初始化的)sem_t可能具有确切的内容作为初始化的sem_t - 如果手册是正确的,那么sem_init()只是不起作用。

2 个答案:

答案 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设计师都必须权衡利弊:

  • 这对API的实现者来说是一个额外的负担吗?
  • 否则可以实现吗? (即它必须在图书馆/系统级别实施)
  • 它是否会分散API核心功能?
  • 选择的语言是否被认为是惯用的?
  • 一般来说它被认为是API的好模式吗?
  • 容易出错/是否存在安全风险?
  • ...

在这种情况下,似乎允许双重初始化将通过大多数这些标准获得。所以决定不允许它。它可能仍适用于您的特定实现,编译器,系统甚至大多数实现,编译器,系统。

如何表达?好吧,你在手册中称之为未定义的行为,并且每个人都知道不要这样做。对环境有良好工作直觉的人很容易猜测行为可能是什么。但是,只有傻瓜会依赖它。

  
    

(未初始化的)sem_t可以具有确切的内容作为初始化的sem_t

  

这是事实。但是,我们假设sem_t拥有指向sem_init使用malloc分配的一堆堆内存的指针。随机非初始化sem_t完全有可能具有完全相同的指针值,但它对应的资源将不存在。