每一個信號都有一個編號和一個宏定義名稱 ,這些宏定義能夠在 signal.h 中找到。html
#include <signal.h> typedef void( *sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler);
signal函數的做用:給某一個進程的某一個特定信號(標號爲signum)註冊一個相應的處理函數,即對該信號的默認處理動做進行修改,修改成handler函數所指向的方式。bash
舉個代碼例子:函數
#include<stdio.h> #include<signal.h> void handler(int sig) { printf("get a sig,num is %d\n",sig); } int main() { signal(2,handler); while(1) { sleep(1); printf("hello\n"); } return 0; }
修改了2號信號(Ctrl-c)的默認處理動做爲handler函數的內容,則當該程序在前臺運行時,鍵入Ctrl-c後不會執行它的默認處理動做(終止該進程)spa
進程收到一個信號後不會被當即處理,而是在恰當 時機進行處理!什麼是適當的時候呢?好比說中斷返回的時候,或者內核態返回用戶態的時候(這個狀況出現的比較多)。操作系統
信號不必定會被當即處理,操做系統不會爲了處理一個信號而把當前正在運行的進程掛起(切換進程),掛起(進程切換)的話消耗太大了,若是不是緊急信號,是不會當即處理的。操做系統多選擇在內核態切換回用戶態的時候處理信號,這樣就利用二者的切換來處理了(不用單獨進行進程切換以避免浪費時間)。.net
總歸是不能避免的,由於頗有可能在睡眠的進程就接收到信號,操做系統確定不肯意切換當前正在運行的進程,因而就得把信號儲存在進程惟一的PCB(task_struct)當中。指針
/************************************************************************* > File Name: test.c > Author:Lynn-Zhang > Mail: iynu17@yeah.net > 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; }
kill命令是調用kill函數實現的。kill函數能夠給一個指定的進程發送指定信號。htm
raise函數可 以給當前進程發送指定的信號 (本身給本身發信號 )對象
#include<signal.h> int kill(pid_t pid,int signo); int raise(int signo);
#include<stdlib.h> void abort(void);
/************************************************************************* > File Name: alarm.c > Author:Lynn-Zhang > Mail: iynu17@yeah.net > 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信號, 該信號的默認處理動做是終止當前進程。blog
該程序會在1秒鐘以內不停地數數,並打印計數器,1秒鐘到了就被SIGALRM信號終止。因爲電腦配置等的不一樣,每臺電腦一秒鐘以內計數值是不一樣的通常是不一樣的。
#include <unistd.h> unsigned int alarm(unsigned int seconds);
alarm函數的返回值是0或上次設置鬧鐘剩餘的時間。
1.信號在內核中的表示:
每一個信號都由兩個標誌位分別表示阻塞和未決,以及一個函數指針表示信號的處理動做。
在上圖的例子中,
1. SIGHUP信號未阻塞也未產生過,當它遞達時執行默認處理動做。
2. SIGINT信號產生過,但正在被阻塞,因此暫時不能遞達。雖然它的處理動做是忽略,但在沒 有解除阻塞以前不能忽略這個信號,由於進程仍有機會改變處理動做以後再解除阻塞。
3. SIGQUIT信號未產生過,一旦產生SIGQUIT信號將被阻塞,它的處理動做是用戶自定義函數sighandler。阻塞信號集也叫做信號屏蔽字。
信號產生可是不當即處理,前提條件是要把它保存在pending表中,代表信號已經產生。
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解除了對當前若干個未決信號的阻塞,則在sigprocmask返回前,至少將其中 一個信號遞達。
參數解析:
how,有三個宏
set表示新設置的信號屏蔽字,oset表示當前信號屏蔽字
處理方式:
4. sigpending讀取當前進程的未決信號集,經過set參數傳出
#include <signal.h> int sigpending(sigset_t *set);
這是一個輸出型參數,會把當前進程的pending表打印到傳入的set集中。
實例驗證上面幾個函數:
一開始沒有任何信號,因此pending表中全是0,我經過Ctrl+C傳入2號信號,看到pending表中有2號被置位了,通過10秒取消阻塞,2號信號被處理(通過我自定義的函數)