信號的基本概念: 每個信號都有一個編號和一個巨集定義名稱 ,這些巨集定義可以在 signal.h 中找到。 使用kill -l命令查看系統中定義的信號列表: 1-31是普通信號; 34-64是實時信號 所有的信號都由操作系統來發! 對信號的三種處理方式:忽略信號、直接執行進程對於該信號的預設動作、執行自 ...
信號的基本概念: 每個信號都有一個編號和一個巨集定義名稱 ,這些巨集定義可以在 signal.h 中找到。 使用kill -l命令查看系統中定義的信號列表: 1-31是普通信號; 34-64是實時信號 所有的信號都由操作系統來發! 對信號的三種處理方式:忽略信號、直接執行進程對於該信號的預設動作、執行自定義動作;
產生信號的條件:
1.用戶在終端按下某些鍵時,終端驅動程式會發送信號給前臺程式。 例如:Ctrl-c產生SIGINT信號,Ctrl-\產生SIGQUIT信號,Ctrl-z產生SIGTSTP信號 2.硬體異常產生信號。 這類信號由硬體檢測到並通知內核,然後內核向當前進程發送適當的信號。 例如:當前進程執行除以0的指令,CPU的運算單元會產生異常,內核將這個進程解釋為SIGFPE信號發送給當前進程。 當前進程訪問了非法記憶體地址,MMU會產生異常,內核將這個異常解釋為SIGSEGV信號發送給進程。 3.一個進程調用kill(2)函數可以發送信號給另一個進程。 可以用kill(1)命令發送信號給某個進程,kill(1)命令也是調用kill(2)函數實現的,如果不明確指定信號則發送SIGTERM信號,該信號的預設處理動作是終止進程。 信號 的產生: 1.通過終端按鍵產生信號 舉個慄子:寫一個死迴圈,前臺運行這個程式,然後在終端鍵入Ctrl-c 當CPU正在執行這個進程的代碼 , 終端驅動程式發送了一 個 SIGINT 信號給該進程,記錄在該進程的 PCB中,則該進程的用戶空間代碼暫停執行 ,CPU從用戶態 切換到內核態處理硬體中斷。 從內核態回到用戶態之前, 會先處理 PCB中記錄的信號 ,發現有一個 SIGINT 信號待處理, 而這個信號的預設處理動作是終止進程,所以直接終止進程而不再返回它的用戶空間代碼執行。 2.調用系統函數向進程發信號/************************************************************************* > File Name: test.c > Author:Lynn-Zhang > Mail: [email protected] > Created Time: Fri 15 Jul 2016 03:03:57 PM CST ************************************************************************/ #include<stdio.h> int main() { printf("get pid :%d circle ...\n",getpid()); while(1); return 0; }寫一個上面的程式在後臺執行死迴圈,並獲取該進程的id,然後用kill命令給它發送SIGSEGV信號,可以使進程終止。也可以使用kill -11 5796,11是信號SIGSEGV的編號。
kill命令是調用kill函數實現的。kill函數可以給一個指定的進程發送指定信號。
raise函數可 以給當前進程發送指定的信號 (自己給自己發信號 )
#include<signal.h> int kill(pid_t pid,int signo); int raise(int signo);這兩個函數都是成功返回0,錯誤返回-1. 除此之外,abort函數使當前進程接收到SIGABRT信號而異常終止。
#include<stdlib.h> void abort(void);就像 exit函數一樣 ,abort 函數總是會成功的 ,所以沒有返回值。 3.由軟體條件產生信號
/************************************************************************* > File Name: alarm.c > Author:Lynn-Zhang > Mail: [email protected] > Created Time: Fri 15 Jul 2016 08:52:02 PM CST ************************************************************************/ #include<stdio.h> int main() { int count=0; alarm(1); while(1) { printf("%d\n",count); count++; } return 0; }
通過實現以上代碼,調用alarm函數可以設定一個鬧鐘,告訴內核在seconds秒之後給當前進程發SIGALRM信號, 該信號的預設處理動作是終止當前進程。
該程式會在1秒鐘之內不停地數數,並列印計數器,1秒鐘到了就被SIGALRM信號終止。由於電腦配置等的不同,每臺電腦一秒鐘之內計數值是不同的一般是不同的。
#include <unistd.h> unsigned int alarm(unsigned int seconds);
alarm函數的返回值是0或上次設置鬧鐘剩餘的時間。
阻塞信號:
1.信號在內核中的表示:
信號遞達delivery:實際執行信號處理信號的動作 信號未決pending:信號從產生到抵達之間的狀態,信號產生了但是未處理 忽略:抵達之後的一種 動作 阻塞block:收到信號不立即處理 被阻塞的信號將保持未決狀態,直到進程解除對此信號的阻塞,才執行抵達動作 信號產生和阻塞沒有直接關係 抵達和解除阻塞沒有直接關係! 進程收到一個信號後,不會立即處理,它會在恰當的時機被處理。
每個信號都由兩個標誌位分別表示阻塞和未決,以及一個函數指針表示信號的處理動作。
在上圖的例子中,
1. SIGHUP信號未阻塞也未產生過,當它遞達時執行預設處理動作。
2. SIGINT信號產生過,但正在被阻塞,所以暫時不能遞達。雖然它的處理動作是忽略,但在沒 有解除阻塞之前不能忽略這個信號,因為進程仍有機會改變處理動作之後再解除阻
塞。
3. SIGQUIT信號未產生過,一旦產生SIGQUIT信號將被阻塞,它的處理動作是用戶自定義函數sighandler。
阻塞信號集也叫作信號屏蔽字。
2.信號集操作函數
#include <signal.h> int sigemptyset(sigset_t *set); //初始化set所指向的信號集,使所有信號的對應位清0 int sigfillset(sigset_t *set); //初始化set所指向的信號集,表示該信號集的有效信號包括系統支持的所有信號 int sigaddset(sigset_t *set, int signo); //在該信號集中添加有效信號 int sigdelset(sigset_t *set, int signo); //在該信號集中刪除有效信號 int sigismember(const sigset_t *set, int signo); //⽤用於判斷一個信號集的有效信號中是否包含某種信號
3.調用函數sigprocmask可以讀取或更改進程的信號屏蔽字(阻塞信號集)。
#include <signal.h> int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
如果調用sigprocmask解除了對當前若幹個未決信號的阻塞,則在sigprocmask返回前,至少將其中 一個信號遞達。
4. sigpending讀取當前進程的未決信號集,通過set參數傳出
#include <signal.h> int sigpending(sigset_t *set);