Linux系統編程(23)——訊號的阻塞,linux23
實際執行訊號的處理動作稱為訊號遞達(Delivery),訊號從產生到遞達之間的狀態,稱為訊號未決(Pending)。進程可以選擇阻塞(Block)某個訊號。被阻塞的訊號產生時將保持在未決狀態,直到進程解除對此訊號的阻塞,才執行遞達的動作。注意,阻塞和忽略是不同的,只要訊號被阻塞就不會遞達,而忽略是在遞達之後可選的一種處理動作。訊號在核心中的表示可以看作是這樣的:
每個訊號都有兩個標誌位分別表示阻塞和未決,還有一個函數指標表示處理動作。訊號產生時,核心在進程式控制制塊中設定該訊號的未決標誌,直到訊號遞達才清除該標誌。在的例子中,
SIGHUP訊號未阻塞也未產生過,當它遞達時執行預設處理動作。
SIGINT訊號產生過,但正在被阻塞,所以暫時不能遞達。雖然它的處理動作是忽略,但在沒有解除阻塞之前不能忽略這個訊號,因為進程仍有機會改變處理動作之後再解除阻塞。
SIGQUIT訊號未產生過,一旦產生SIGQUIT訊號將被阻塞,它的處理動作是使用者自訂函數sighandler。
如果在進程解除對某訊號的阻塞之前這種訊號產生過多次,將如何處理?POSIX.1允許系統遞送該訊號一次或多次。Linux是這樣實現的:常規訊號在遞達之前產生多次只計一次,而即時訊號在遞達之前產生多次可以依次放在一個隊列裡。本章不討論即時訊號。從來看,每個訊號只有一個bit的未決標誌,非0即1,不記錄該訊號產生了多少次,阻塞標誌也是這樣表示的。因此,未決和阻塞標誌可以用相同的資料類型sigset_t來儲存,sigset_t稱為訊號集,這個類型可以表示每個訊號的“有效”或“無效”狀態,在阻塞訊號集中“有效”和“無效”的含義是該訊號是否被阻塞,而在未決訊號集中“有效”和“無效”的含義是該訊號是否處於未決狀態。下一節將詳細介紹訊號集的各種操作。阻塞訊號集也叫做當前進程的訊號屏蔽字(Signal Mask),這裡的“屏蔽”應該理解為阻塞而不是忽略。
訊號集操作函數
sigset_t類型對於每種訊號用一個bit表示“有效”或“無效”狀態,至於這個類型內部如何儲存這些bit則依賴於系統實現,從使用者的角度是不必關心的,使用者只能調用以下函數來操作sigset_t變數,而不應該對它的內部資料做任何解釋,比如用printf直接列印sigset_t變數是沒有意義的。
#include <signal.h> int sigemptyset(sigset_t *set);int sigfillset(sigset_t *set);int sigaddset(sigset_t *set, int signo);int sigdelset(sigset_t *set, int signo);int sigismember(const sigset_t *set, intsigno);
函數sigemptyset初始化set所指向的訊號集,使其中所有訊號的對應bit清零,表示該訊號集不包含任何有效訊號。函數sigfillset初始化set所指向的訊號集,使其中所有訊號的對應bit置位,表示該訊號集的有效訊號包括系統支援的所有訊號。注意,在使用sigset_t類型的變數之前,一定要調用sigemptyset或sigfillset做初始化,使訊號集處於確定的狀態。初始化sigset_t變數之後就可以在調用sigaddset和sigdelset在該訊號集中添加或刪除某種有效訊號。這四個函數都是成功返回0,出錯返回-1。sigismember是一個布爾函數,用於判斷一個訊號集的有效訊號中是否包含某種訊號,若包含則返回1,不包含則返回0,出錯返回-1。
每個進程都有一個用來描述哪些訊號遞送到進程時將被阻塞的訊號集,該訊號集中的所有訊號在遞送到進程後都將被阻塞。下面是與訊號阻塞相關的幾個函數:
#include <signal.h>int sigprocmask(int how, const sigset_t *set, sigset_t *oldset));int sigpending(sigset_t *set));int sigsuspend(const sigset_t *mask));
sigprocmask函數
調用函數sigprocmask可以讀取或更改進程的訊號屏蔽字。
#include <signal.h>
int sigprocmask(int how, const sigset_t*set, sigset_t *oset);傳回值:若成功則為0,若出錯則為-1
如果oset是非null 指標,則讀取進程的當前訊號屏蔽字通過oset參數傳出。如果set是非null 指標,則更改進程的訊號屏蔽字,參數how指示如何更改。如果oset和set都是非null 指標,則先將原來的訊號屏蔽字備份到oset裡,然後根據set和how參數更改訊號屏蔽字。
sigprocmask()函數能夠根據參數how來實現對訊號集的操作,操作主要有三種:
參數how 進程當前訊號集
SIG_BLOCK 在進程當前阻塞訊號集中添加set指向訊號集中的訊號
SIG_UNBLOCK 如果進程阻塞訊號集中包含set指向訊號集中的訊號,則解除對該訊號的阻塞
SIG_SETMASK 更新進程阻塞訊號集為set指向的訊號集
如果調用sigprocmask解除了對當前若干個未決訊號的阻塞,則在sigprocmask返回前,至少將其中一個訊號遞達。
sigpending函數
#include <signal.h> int sigpending(sigset_t *set);
sigpending讀取當前進程的未決訊號集,通過set參數傳出。調用成功則返回0,出錯則返回-1。
sigsuspend(const sigset_t *mask))用於在接收到某個訊號之前,臨時用mask替換進程的訊號掩碼, 並暫停進程執行,直到收到訊號為止。sigsuspend 返回後將恢複調用之前的訊號掩碼。訊號處理函數完成後,進程將繼續執行。該系統調用始終返回-1,並將errno設定為EINTR。
結構體itimerval:
struct itimerval { struct timeval it_interval; /*next value */ struct timeval it_value; /* current value */ }; struct timeval { long tv_sec; /* seconds */ long tv_usec; /* microseconds */ };
三參數訊號處理函數中第二個參數的說明性描述:
siginfo_t { int si_signo; /* 訊號值,對所有訊號有意義*/
int si_errno; /* errno值,對所有訊號有意義*/
int si_code; /* 訊號產生的原因,對所有訊號有意義*/
pid_t si_pid; /* 發送訊號的進程ID,對kill(2),即時訊號以及SIGCHLD有意義 */
uid_t si_uid; /* 發送訊號進程的真實使用者ID,對kill(2),即時訊號以及SIGCHLD有意義 */
int si_status; /* 退出狀態,對SIGCHLD有意義*/
clock_t si_utime; /* 使用者消耗的時間,對SIGCHLD有意義 */
clock_t si_stime; /* 核心消耗的時間,對SIGCHLD有意義 */
sigval_t si_value; /* 訊號值,對所有即時有意義,是一個聯合資料結構,
/*可以為一個整數(由si_int標示,也可以為一個指標,由si_ptr標示)*/ void * si_addr; /* 觸發fault的記憶體位址,對SIGILL,SIGFPE,SIGSEGV,SIGBUS訊號有意義*/
int si_band; /* 對SIGPOLL訊號有意義 */
int si_fd; /* 對SIGPOLL訊號有意義 */
}
實際上,除了前三個元素外,其他元素組織在一個聯合結構中,在聯合資料結構中,又根據不同的訊號組織成不同的結構。注釋中提到的對某種訊號有意義指的是,在該訊號的處理函數中可以訪問這些域來獲得與訊號相關的有意義的資訊,只不過特定訊號只對特定資訊感興趣而已。
下面是一個例子:
#include <signal.h>#include <stdio.h> void printsigset(const sigset_t *set){ inti; for(i = 1; i < 32; i++) if(sigismember(set, i) == 1) putchar('1'); else putchar('0'); puts("");} int main(void){ sigset_ts, p; sigemptyset(&s); sigaddset(&s,SIGINT); sigprocmask(SIG_BLOCK,&s, NULL); while(1) { sigpending(&p); printsigset(&p); sleep(1); } return0;}
程式運行時,每秒鐘把各訊號的未決狀態列印一遍,由於我們阻塞了SIGINT訊號,按Ctrl-C將會使SIGINT訊號處於未決狀態,按Ctrl-\仍然可以終止程式,因為SIGQUIT訊號沒有阻塞。
對於linux系統編程的學習
建議:
1. 以Redhat Enterprise Linux 5 (RHEL5)為例,系統支援多種程式設計語言,主要看你習慣那種程式設計語言,不過個人推薦:C/C++語言進行編程;
2. 推薦圖書:
2.1. 《Linux系統與網路服務管理 技術大全(第二版)》楊明華 譚勵等編著 電子工業出版社 79元 含光碟片一張;
2.2. 《Linux系統管理與網路管理》 餘柏山等編著 清華大學出版社 89元 含光碟片一張。
讀完這兩本書,基本上就夠用了。
建議:
1. 最好在系統中先安裝VMware虛擬機器,然後在虛擬機器中安裝Redhat Enterprise Linux 5等Linux系統;
2. 在虛擬機器的Linux系統中進行各種測試,這樣就算有問題也沒有什麼關係。
linux shell 編程 查看系統全部訊號資訊 子程式怎寫 ?
你要察看什麼訊號? 是系統的基本資料嗎?察看系統資訊可以用uname:
$ uname -a
Linux qunero-Ubuntu-desktop 3.0.0-12-generic #20-Ubuntu SMP Fri Oct 7 14:50:42 UTC 2011 i686 i686 i386 GNU/Linux