浅谈Linux中的信号处理机制(三)
? ? ? ?一晃眼,已經到9月底了,都來不及去感慨時間匆匆。最近常常會想明年的今天我將會在那里干著什么樣的工作?對未來又是憧憬又是擔憂,壓力山大。無論如何現在還是踏踏實實的學習吧,能這樣安安靜靜學習的日子也不多了。不扯了,還是接著前面的寫吧。
SA_RESTART語義? ? ? ?在上篇提到過,SA_RESTART標志的作用是重啟系統調用。其作用是建立在這樣的基礎上的:在Linux系統上,如果進程正在執行一個低速系統調用期間捕捉到一個信號,那么該系統調用會被中斷,在處理完信號之后,這個系統調用將不會繼續執行。隨后返回錯誤,errno被設置為EINTR。所謂的慢速系統調用包括但不局限于以下:
以我現在的功力總結全面是不可能的,平時當我們遇上進程要處理會阻塞的系統調用時,就需要留個心眼兒,要考慮一下被信號中斷的情況。在不使用SA_RESTART的時候,我們要重啟系統調用時,可以這樣組織代碼:
int cnt; while((cnt = read(fd,buf,BUFSIZE))==-1 && errno== EINTR) //read()如果被中斷返回錯誤,就會自動重啟continue; ... if(cnt == -1)exit(-1); //其他使read()出錯的情況我反正是不喜歡的這樣的代碼風格的,有了SA_RESTART這個標志,我們本可以把代碼寫得更加優雅:
#include <errno.h> #include <signal.h> #include <unistd.h> #define BUFSIZE 1024void handler(int sig) { }int main() {struct sigaction act;act.sa_flags = SA_RESTART;sigemptyset(&act.sa_mask);act.sa_handler = handler;if(sigaction(SIGINT,&act,0) == -1)exit(-1); char buf[BUFSIZE] = {0};read(0,buf,BUFSIZE-1);write(1,buf,BUFSIZE); }在之前的一篇博客上,曾使用過這個標志,應該說這個標志位還是比較常用的一個,特別是在socket編程中。
可重入函數與不可重入函數? ? ? 在《c++11 Thread庫之原子操作》中提到了多線程程序中多個線程之間數據共享所引起的問題。其實在有信號處理的程序中也存在著這樣的問題,因為信號可能會在程序執行的某一時間點異步中斷程序,轉而去執行信號處理函數。和多線程程序一樣,這時候程序就有了兩個執行的線程,雖然不是并發的。如果一個進程的多條線程可以同時安全地(能產生預期的效果)執行某一函數,那么我們稱這個函數是可重入函數,反之則為不可重入函數。
? ? ? 我做了一個gif圖來表示不可重入函數,就拿我們最熟悉的printf()函數來舉例,我們已經知道printf()函數是行緩沖的IO函數,而這個緩沖區是一個全局的buffer。當主線程中正在執行printf()的時候,一個信號過來了,那么進程會把這個當前線程暫停,轉而去執行信號處理函數,恰巧這個信號處理函數中,也調用了printf()函數,于是buf就被修改了(圖中用變了顏色來表示),當信號處理函數返回以后,主線程恢復執行,而此時它正在使用的buf已經不是之前的那個buf了。于是可能會出現一些意料之外的輸出。
一般來講,更新全局數據結構的函數,是不可重入的函數。通常有這幾類:
?當我們所編寫的函數要更新全局變量該怎么辦呢?sig_atomic_t這種數據類型是C語言標準所規定的一種原子操作的數據類型,關于原子操作的內容可移步:《c++11 Thread庫之原子操作》。具體用法和c++11中的std::atomic類型類似,不再贅述。值得一提的是,使用這個數據類型時,應當使用volatile關鍵字聲明,以防止編譯器把其優化到寄存器之中。
GDB調試與信號? ? ? 在使用gdb調試程序時,缺省情況下信號會被gdb截獲,導致要調試的程序無法接收到信號,我們可以使用info handle來查看信號的缺省處理方式,同樣info signals可以查看接受到的信號。要想在調試的程序中使用信號,我們需要使用gdb中的handle這個命令,具體用法如這個形式 ? :handle ?signal keywords。keywords的取值如下:
| keywords | 說明 | keywords | 說明 |
| stop | 當GDB收到signal,停止被調試程序的執行 | nostop | 當GDB收到指定的信號,不會應用停止程序的執行,只會打印出一條收到信號的消息 |
| 如果收到signal,打印出一條信息 | noprint | 不會打印信息 | |
| pass | 如果收到signal,把該信號通知給被調試程序 | nopass | 不會告知被調試程序收到signal |
| ignore | 同nopass | noignore | 同pass |
? ? handle命令還是比較簡單的,設置完以后,可以像普通的程序那樣調試了。
? ? 關于信號暫時先總結這么多吧,以后用到了什么再慢慢往里邊塞吧!
轉載于:https://www.cnblogs.com/ittinybird/p/4845394.html
總結
以上是生活随笔為你收集整理的浅谈Linux中的信号处理机制(三)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: iOS 9 学习系列:UIStack V
- 下一篇: android5.0(Lollipop)