sem_init重复调用引发sem_wait线程无法被唤醒

问题

一段老代码,两个线程,一个线程调用sem_wait等待信号量,另外一个线程在某失败分支会调用sem_init清信号量,结果导致sem_wait线程无法被唤醒;

分析

Linux manpage

从描述中可见,初始化一个已经被初始化的信号量会导致未定义行为;

glibc源码

到底会发生什么未定义行为,我们直接看源码吧;

首先,对比结构体,旧结构体只有value成员,新结构体中增加了private和nwaiters成员;nwaiters成员会在调用sem_wait时候增加;

然后,对比sem_post唤醒函数;可见,新唤醒函数会在唤醒操作执行之前对nwaiters进行判断,只有当nwaiters>0时,才进行唤醒;

而旧的唤醒操作,则没有类似判断;

现在,我们清楚了,老代码用的老版本的glibc,内部没有等待判断,一直没有出问题,而使用新版本的glibc之后,加入了判断,就有问题了;

结论

  1. sem_init是用来在初始化的时候调用初始化信号量的,并不是用来将信号量清零的;
  2. 重复调用sem_init的行为可能导致已经处于sem_wait的线程无法被唤醒;
  3. 旧版本的glibc机制比较弱,所以老代码一直运行很好;但是新glibc作了检查,所以会出问题;
  4. 按照目前代码看,如果单个线程自己在调用了sem_wait之后再调用sem_init时没什么影响的;但是不保证以后的glibc会再做什么修改造成影响;
  5. 除了初始化阶段,其他流程中不要使用sem_init;
  6. 最好使用其他方式替代信号量,比如条件变量;

本文链接:sem_init重复调用引发sem_wait线程无法被唤醒

转载声明:转载请注明来源:Linux TCP/IP Stack,谢谢!


发表评论

电子邮件地址不会被公开。 必填项已用*标注