前言
曾經的 UNIX 系統中,訊號的不可靠的。什麼是不可靠?就是訊號丟失唄。那什麼是訊號丟失?就是當系統正在處理某個事務的時候,如果收到了某個訊號,但它不能及時處理這個訊號,那麼只能忽略掉此訊號。
而在可靠訊號機制中,如果發生了上述情況,則要求系統處理完當前的事務後,還能夠找回丟失的那個訊號。也就是說,要具備本文即將講述的 - 訊號的阻塞功能。
訊號的阻塞
系統在訊號發生後,會在程式表中設定一個標誌。在訊號發生到程式表中設定一個標誌的時間間隔之內,我們稱訊號是未決的。所謂訊號阻塞,就是指將訊號保持未決狀態,直到使用者將訊號解除阻塞,該訊號才被接收。
重要概念:訊號集
訊號集是一種資料型別,它能夠表述系統內所有的訊號:類似點陣圖,某位為 1 表示該位對應的訊號存在(被遮蔽),反之不存在(未被遮蔽)。對此資料型別的操作需要使用下表所提供的函式來進行:
大致步驟
1. 儲存當前訊號遮蔽集
2. 定義新的訊號遮蔽集
3. 使新的訊號遮蔽集生效
4. 恢復原來的訊號遮蔽集
這四個步驟,所涉及到的只有一個函式:sigprocmask 函式,該函式的詳細說明請參考相關文件。
程式碼實現
如下示例程式首先執行上述的步驟 1 2 3( 遮蔽退出訊號 ),然後掛起 5 秒鐘,然後在此期間產生 3 次退出訊號,再執行上述步驟 4,最後再掛起 5 秒鐘:
1 #include <stdio.h> 2 #include <unistd.h> 3 #include <signal.h> 4 5 // 訊號處理函式( 此類函式都是無返回且形參為一個整數,該整數即為所接收到的訊號 ) 6 static void sig_quit (int); 7 8 int main (void) { 9 10 // 定義訊號集 11 sigset_t newmask, oldmask, pendmask; 12 13 // 註冊退出訊號處理函式 14 if (signal(SIGQUIT, sig_quit) == SIG_ERR) { 15 printf("註冊訊號處理函式失敗\n"); 16 return 1; 17 } 18 19 // 建立一個新的訊號遮蔽字 20 sigemptyset(&newmask); 21 sigaddset(&newmask, SIGQUIT); 22 23 // 遮蔽退出訊號,並將原來的訊號遮蔽字儲存起來。 24 if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0) { 25 printf("遮蔽退出訊號失敗\n"); 26 return 2; 27 } 28 29 printf("已遮蔽退出訊號\n"); 30 31 sleep(5); 32 33 // 恢復原來的訊號遮蔽字 34 printf("\n接下來恢復原來的訊號遮蔽字\n"); 35 if (sigprocmask(SIG_SETMASK, &oldmask, 0) < 0) { 36 printf("恢復訊號遮蔽字失敗\n"); 37 return 2; 38 } 39 40 sleep(5); 41 42 return 0; 43 } 44 45 static void sig_quit (int signo) 46 { 47 printf("捕捉到退出訊號\n"); 48 49 // 將對退出訊號的處理設定為關閉程式 50 if (signal(SIGQUIT, SIG_DFL) == SIG_ERR) { 51 printf("設定退出訊號處理函式失敗\n"); 52 } 53 }
執行測試
第一次睡眠時,我發出了三次退出訊號,結果只接收並處理了一次( 假如接收了兩次,則第二次就會結束掉程式了。)。
當恢復到之前的訊號遮蔽字以後,我再發出退出訊號,程式立刻就關閉了。
小結
1. 可靠訊號可靠之處在於可以使訊號阻塞,到合適的時候再解除阻塞,接收訊號。
2. 使用 sigaction 函式實現可靠訊號更加方便。