信號的基本概念:每個信號都有一個編號和一個宏定義名稱 ,這些宏定義可以在 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信號,該信號的默認處理動作是終止進程。
信號 的產生:[align=left]1.通過終端按鍵產生信號[/align]
[align=left]舉個栗子:寫一個死循環,前台運行這個程序,然後在終端鍵入Ctrl-c[/align]
[align=left] 當CPU正在執行這個進程的代碼 , 終端驅動程序發送了一 個 SIGINT 信號給該進程,記錄在該進程的 PCB中,則該進程的用戶空間代碼暫停執行 ,CPU從用戶態 切換到內核態處理硬件中斷。[/align]
[align=left] 從內核態回到用戶態之前, 會先處理 PCB中記錄的信號 ,發現有一個 SIGINT 信號待處理, 而這個信號的默認處理動作是終止進程,所以直接終止進程而不再返回它的用戶空間代碼執行。[/align]
[align=left] 2.調用系統函數向進程發信號[/align]
/************************************************************************* > 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; }
[align=left]寫一個上面的程序在後台執行死循環,並獲取該進程的id,然後用kill命令給它發送SIGSEGV信號,可以使進程終止。也可以使用kill -11 5796,11是信號SIGSEGV的編號。[/align]
[align=left]打開終端1,運行程序:[/align]
[align=left] 利用終端2,給進程發送信號[/align]
[align=left] 終端1 顯示進程被core了:[/align]
kill命令是調用kill函數實現的。kill函數可以給一個指定的進程發送指定信號。raise函數可 以給當前進程發送指定的信號 (自己給自己發信號 )
#include<signal.h> int kill(pid_t pid,int signo); int raise(int signo);[align=left]這兩個函數都是成功返回0,錯誤返回-1.[/align]
[align=left]除此之外,abort函數使當前進程接收到SIGABRT信號而異常終止。[/align]
#include<stdlib.h> void abort(void);[align=left]就像 exit函數一樣 ,abort 函數總是會成功的 ,所以沒有返回值。[/align]
[align=left]3.由軟件條件產生信號[/align]
/************************************************************************* > 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);