首页 > 代码库 > 信号处理方法的问题

信号处理方法的问题

这周有位新同事请我帮忙看一个关于信号处理的问题,程序希望在收到一个信号后退出,而他在信号处理方法里却做了很多事,包含释放一些全局内存等。


这样问题就产生了,程序不定时的就挂死了,用gdb一看,全部的线程都挂在了pthread_once方法里,而似乎每一个线程都在处理信号,当中产生问题的线程堆栈例如以下:


Thread 1 (Thread 0x7f41252f3720 (LWP 31542)):
#0 0x000000339860cb1b in pthread_once () from /lib64/libpthread.so.0
#1 0x00000033982fd6f4 in backtrace () from /lib64/libc.so.6
#2 0x000000339826fa4b in __libc_message () from /lib64/libc.so.6
#3 0x0000003398275366 in malloc_printerr () from /lib64/libc.so.6
#4 0x0000003398278de4 in _int_malloc () from /lib64/libc.so.6
#5 0x0000003398279b91 in malloc () from /lib64/libc.so.6
#6 0x00007f41253b40bd in operator new(unsigned long) () from /usr/lib64/libstdc++.so.6
#7 0x00007f41253b41d9 in operator new[](unsigned long) () from /usr/lib64/libstdc++.so.6
---Type <return> to continue, or q <return> to quit---
#8 0x000000000045f86a in log4cpp::StringUtil::vform(char const*, __va_list_tag*) ()
#9 0x000000000044eb69 in log4cpp::Category::_logUnconditionally(int, char const*, __va_list_tag*) ()
#10 0x000000000044f4af in log4cpp::Category::warn(char const*, ...) ()
#11 0x00000000004431a1 in singalHandler(int) ()
#12 <signal handler called>
#13 0x000000339860cb19 in pthread_once () from /lib64/libpthread.so.0
#14 0x00000033982fd6f4 in backtrace () from /lib64/libc.so.6
#15 0x000000339826fa4b in __libc_message () from /lib64/libc.so.6
#16 0x0000003398275366 in malloc_printerr () from /lib64/libc.so.6
#17 0x0000003398278de4 in _int_malloc () from /lib64/libc.so

问题在哪里呢?似乎全部开源码里,都少有人在信号处理方法里写大量代码的,这是为什么呢?

原因在于,信号是可能在随意时刻打断你线程的正在运行代码,信号处理方法插入进去运行时,就可能造成有些函数被重复重入。比如上面这个样例中,thead1正在new一个对象,运行malloc分配内存的过程中,突然被信号打断,而信号处理方法里竟然又有malloc过程,而malloc是不能重复重入的!于是导致挂死。

还有一个问题的,子进程会继承父进程的非常多资源,当中就包含信号,他的程序处理信号后,才pthread_create很多工作线程,并且,没有屏蔽信号,所以,全部的线程都在处理那个信号处理方法,全部线程都挂死了。

解决方法有非常多种,一般是在信号处理方法里仅仅做少量工作,通知其它线程自我回收资源。
对于多线程程序来说,仅仅弄一个线程使用堵塞式信号处理方法,专职的处理信号,这样更符合多线程的设计精神。比如,在派生子线程前,用pthread_sigmask来设置信号不会打断子线程的运行,而在主线程里,使用堵塞的sigwait方法来同步处理信号,在这里能够处理一些复杂的操作,不用操心“重入”问题。

信号处理方法的问题