在上篇博客中,主要介紹了信號的基本概念,這次我主要介紹一下與信號相關的函數以及對它們的應用。
sigaction函數
#include <signal.h>
int sigaction(int signo, const struct sigaction *act, struct sigaction *oact);
sigaction函數可以讀取和修改與指定信號相關聯的處理動作。
調用成功則返回0,出錯則返回- 1。
signo是指定信號的編號。
若act指針非空,則根據act修改該信號的處理動作。
若oact指針非空,則通過oact傳出該信號原來的處理動作。
act和oact指向sigaction結 構體:
pause函數
#include <unistd.h>
int pause(void);
pause函數使調用進程掛起直到有信號遞達。如果信號的處理動作是終止進程,則進程終止,pause函數沒有機會返回;如果信號的處理動作是忽略,則進程繼續處於掛起狀態,pause 不返回;如果信號的處理動作是捕捉,則調用了信號處理函數之後pause返回-1,errno設置為 EINTR, 所以pause只有出錯的返回值(想想以前還學過什麼函數只有出錯返回值?)。錯誤碼 EINTR表示“被信號中斷”。
{exec函數、pause函數只會出錯返回。。。}
競態條件與sigsuspend函數
由於異步事件在任何時候都有可能發生(這裡的異步事件指出現更高優先級的進程),如果我們寫程序時考慮不周密,就可能由於時序問題 而導致錯誤,這叫做競態條件 (Race Condition)。
和pause一樣,sigsuspend沒有成功返回值,只有執行了一個信號處理函數之後 sigsuspend才返回,返回值為-1,errno設置為EINTR。
調用sigsuspend時,進程的信號屏蔽字由sigmask參數指定,可以通過指定sigmask來臨時解除對某個信號的屏蔽,然後掛起等待,當sigsuspend返回時,進程的信號屏蔽字恢復為原來的值,如果原來對該信號是屏蔽的,從sigsuspend返回後仍然是屏蔽的。
首先,我們來對31個信號進行捕捉,查看有那個信號不能被捕捉:
代碼如下:
我們在另外一個終端中給這個進程發送信號,看那個信號可以是程序結束:
9號信號(SIGKILL):
19號信號(SIGSTOP):
結論:這兩個信號並不能捕捉
實現自己的my_sleep函數:
代碼:
這是我們自己實現的my_sleep函數,看下有沒有什麼問題?
系統運行的時序(Timing)並不像我們寫程序時所設想的那樣。 雖然alarm(timeout)緊接著的下一行就是pause(),但是無法保證pause()一定會在調用alarm(timeout)之後的timeout秒之內被調用。由於異步事件在任何時候都有可能發生,這時由於時序問題就會出現錯誤。
怎樣改進呢?
這時我們就要使用sigsuspend函數,而不是pause函數。
看下面的代碼:
運行結果: