天天看點

信号處理方法的問題

這周有位新同僚請我幫忙看一個關于信号處理的問題,程式希望在收到一個信号後退出,而他在信号處理方法裡卻做了很多事,包含釋放一些全局記憶體等。

這樣問題就産生了,程式不定時的就挂死了,用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方法來同步處理信号,在這裡能夠處理一些複雜的操作,不用操心“重入”問題。

繼續閱讀