多线程下慎用sigwait

简介:
本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途。
作者:gfree.wind@gmail.com
博客:linuxfocus.blog.chinaunix.net
  
   
 

今天帮助同事调试一个问题,最终确定是在多线程下使用sigwait引发的。
该线程大致流程如下:

  1. void thread(void *data)
  2. {
  3.     int wait_sig = *(int*)data;
  4.     sigset_t sigset;

  5.     sigemptyset(&sigset);
  6.     sigaddset(&sigset, wait_ig);
  7.     
  8.     while (1) {
  9.         int signal;
  10.         if (!= sigwait(&sigset, &signal)) {
  11.             break;
  12.         }
  13.     }
  14. }
出现的问题是这个断言失败,也就是说sigwait失败了。后来打印出sigwait的返回值,发现sigwait的失败的原因时EINTR,也就说 sigwait被一个信号中断了,但是不知道信号来自何处。说到此处,先说明一下,sigwait这个函数很奇怪,跟一般的linux API不同。sigwait出错的时候,并不设置errno,而直接把errno错误值返回。

通过增加一个新的判断
 
  1. int ret = sigwait(&sigset, &signal));
  2. if (EINTR == ret) {
  3.     continue;
  4. }
  5. else if (ret) {
  6.     break;
  7. }
毕竟sigwait作为一个阻塞操作,因为收到信号而失败,是可以接受的行为,所以要对EINTR进行特殊处理。
加上这一句后,发现原来在另外一个线程有一个非法内存错误,所以产生了一个SIGSEGV信号。到此,虽然这个问题是由于这个内存问题引起的,但是实际上从这个问题的现象上看,在多线程下使用sigwait容易引起一些问题。

1.在POSIX标准中,当进程收到信号时,如果是多线程的情况,我们是无法确定是哪一个线程处理这个信号。而sigwait是从进程中pending的信号中,取走指定的信号。这样的话,如果要确保sigwait这个线程收到该信号,那么所有线程含主线程以及这个sigwait线程则必须block住这个信号。否则如果在两次sigwait之间,收到了指定信号,该信号很有可能被任意一个线程处理掉。
2.sigwait的名字以及在man中的介绍容易引起人的误解。
1)sigwait,从名字上看只等待指定信号集。这样很容易让人感觉除指定信号外,其他信号不会促使sigwait的返回;
2)来看看man中的说明The sigwait() function suspends execution of the calling thread until the delivery of one of the signals specified in the signal set set.这里也说了,sigwait会一直suspend直到指定信号发生。另外,man中的errors说明也容易引起误解。man中只有一个错误,就是EINVAL,给人感觉sigwait不会被信号中断。
ERRORS
       EINVAL set contains an invalid signal number.
(我的系统是FC12)
但是按照我一直以来的经验,基本上所有的阻塞操作都会被信号中断,除非设置了RESTART标志。所有我建议同事加了以上的代码。
后来看到man中还有这么一句,
NOTES
       sigwait() is implemented using sigtimedwait(2).
于是又查看了sigtimedwait。发现sigtimedwait的手册还是比较完整的。
ERRORS
       EAGAIN No signal in set was delivered within the timeout period specified to sigtimedwait().

       EINTR  The wait was interrupted by a signal handler; see signal(7).  (This handler was for a signal other than one of those in set.)

       EINVAL timeout was invalid.


其实这次的代码是一个开源工具的代码,它使用信号机制作为多线程的通讯工具。个人感觉这种实现并不合适。因为在它的代码中,并没有在所有的线程中屏蔽掉需要sigwait的信号,另外该工具会产生多个线程,每个线程需要sigwait的信号是不同的。按照上面的代码,实际上它只是通过sigwait等到相应的信号来唤醒该线程,那么还不如使用pthread_cond_wait来代替sigwait呢。
目录
相关文章
|
2月前
|
Java 调度
【多线程面试题 四】、 线程是否可以重复启动,会有什么后果?
线程不能被重复启动,一旦调用start()方法后,线程将从新建状态进入就绪状态,再次调用start()会抛出IllegalThreadStateException异常。
|
5月前
|
存储 缓存 安全
Java并发基础之互斥同步、非阻塞同步、指令重排与volatile
在Java中,多线程编程常常涉及到共享数据的访问,这时候就需要考虑线程安全问题。Java提供了多种机制来实现线程安全,其中包括互斥同步(Mutex Synchronization)、非阻塞同步(Non-blocking Synchronization)、以及volatile关键字等。 互斥同步(Mutex Synchronization) 互斥同步是一种基本的同步手段,它要求在任何时刻,只有一个线程可以执行某个方法或某个代码块,其他线程必须等待。Java中的synchronized关键字就是实现互斥同步的常用手段。当一个线程进入一个synchronized方法或代码块时,它需要先获得锁,如果
52 0
|
12月前
|
Java API 调度
多线程的操作
多线程的操作
46 0
在多线程中某段代码只执行一次
使用Automic包中的AtomicBoolean结合CAS来实现。当只希望需要某段代码在多线程中只执行一次的时候,此时你会发现boolean这个变量类型是多么有用。
117 0
|
Linux
Linux驱动开发——并发和竞态(原子操作方式的使用⑤)
Linux驱动开发——并发和竞态(原子操作方式的使用⑤)
160 0
Linux驱动开发——并发和竞态(原子操作方式的使用⑤)
多线程详解p13、join线程强制执行
多线程详解p13、join线程强制执行
|
Java 编译器 测试技术
【高并发】如何使用互斥锁解决多线程的原子性问题?这次终于明白了!
如何解决线程切换带来的原子性问题呢?答案是 保证多线程之间的互斥性。也就是说,在同一时刻只有一个线程在执行! 如果我们能够保证对共享变量的修改是互斥的,那么,无论是单核CPU还是多核CPU,都能保证多线程之间的原子性了。
520 0
【高并发】如何使用互斥锁解决多线程的原子性问题?这次终于明白了!
|
缓存 安全 Java
诡异的并发之可见性
我们都知道,随着祖国越来越繁荣昌盛,随着科技的进步,设备的更新换代,计算机体系结构、操作系统、编译程序都在不断地改革创新,但始终有一点是不变的(我对鸭血粉丝的热爱忠贞不渝):那就是下面三者的性能耗时:CPU < 内存 < I/O
诡异的并发之可见性
|
安全 Java 编译器
诡异并发三大恶人之原子性
上一节阿粉我和大家一起打到了并发中的恶霸可见性,这一节我们继续讨伐三恶之一的原子性。
诡异并发三大恶人之原子性