linux下的信號機制(signal)

1。linux的信號機制
信號時內核提供的一種異步消息機制,用於內核對進程發送異步通知事件,可以理解爲進程執行的流程中的一個軟中斷。
信號可以直接進行用戶空間進程和內核進程之間的交互,內核進程也可以通過它來通知用戶空間進程發送了哪些事件。
一個完整的信號使用由:
(1)內核信號產生
(2)用戶進程信號註冊
(3)信號在用戶進程進程中註銷
(4)執行信號處理函數
常見的信號:
(1)在進程在一個沒有打開的管道上等待時,內核發出SIGPIPE信號
(2)進程在shell中前臺執行時,用戶按下CTRL+C組合,向進程發送SIGINT信號
(3)用戶使用kill命令來向進程發送信號
(4)進程訪問非法的內存地址,內核向進程發送信號SIGSEGV段錯誤
(5)一個進程使用系統調用來向另外一個進程發送信號
(6)發送各種運行異常時,內核向進程發送SIGFPE信號。
2.信號的處理機制
用戶進程對信號的三種處理方式:
(1)忽略信號,即不對信號做任何操作。但是SIGKILL和SIGSTOP信號不能被忽略。
(2)捕捉信號,定義信號處理函數,以便捕捉到這個信號時執行相應的處理函數。
(3)執行默認的操作。
常見的信號的默認操作有:
SIGHUP:終止,該信號在用戶終端結束時發出
SIGINT:終止,通常是用戶鍵入CTRL+C發出的
SIGQUIT:終止,與SIGINT類似,但由CTRL+\發出
SIGILL:當一個進程企圖執行一個非法指令時,發出的信號,使得進程終止
SIFFPE:該信號發生在致命的算術運算錯誤,使得進程終止
SIGKILL:該信號是不能被忽略的信號,使得進程立即終止
SIFALRM:定時器信號
SIGSTOP:該信號不能被忽略,使得進程暫停
SIGCHLD:子進程狀態改變,父進程會受到這個信號,一般默認操作時忽略
SIGABORT:進程異常終止時發出該信號
內核對進程管理的PCB中,包含信號標誌位,若信號發生,則相應位就會被置1.在進程從內核態返回到用戶態時,首先會進行檢查信號相關信息,如果檢測到相應信號的比特位被置1了,則在用戶態中先執行信號處理函數,再返回用戶進程。信號處理函數是在用戶進程之外進行的操作。
3.發送信號
向一個進程中發送信號可以使用函數kill,raise,alarm,pause
默認情況下,kill命令發送SIGTERM以終止一個進程。
(1)kill函數:
#include <signal.h>
#include <sys/types.h>
int kill(pid_t pid,int siq);
(1)pid > 0,表示要發送進程的進程號
(2)pid=0,表示把信號發送到與當前進程同組的所有進程
(3)pid = -1,表示發送到當前的進程表中的所有進程。
(4)pid < -1,表示處於進程組-pid中的所有進程進程。
函數成功返回0,失敗返回-1.
(2)raise函數只允許進程向自身發送信號。
#include <signal.h>
int raise(int sig);
(3)alarm函數
使用alarm函數可以在指定一段時間後給自己本身發送SIGALRM信號,可以作爲定時器使用
#include <unistd.h>
unsigned int alarm(unsigned int seconds); 利用該函數可以實現定時的功能。指定了時間,一到時間就會向進程發送SIGALRM信號,成功返回0,出錯返回-1.
(4)pause函數
#include <unistd.h>
int pause(void);
使進程等待信號。出錯返回-1.
4.信號處理函數
signal函數:
#include <signal.h>
typedef void(*sighander_t)(int);
sighandler_t signal(int signum,singdler_t handler);
signal函數將handler參數指向的函數註冊成爲參數signum所代表的處理函數。signal函數的返回值就是這個信號原來的處理函數,若返回SIG_ERR。則說明有錯誤發生。註冊成功後,所註冊的函數就會被信號處理時進行調用,代替了默認的行爲,稱爲信號被捕捉。
使用signal函數時,應該注意:
(1)handler參數的值可以是SIG_IGN或SIG_DFL,SIG_IGN表示忽略這個信號,SIG_DFL表示對信號的處理重設爲默認的行爲,也可以是自己定義的函數指針。
(2)有些信號是不可以被忽略或者捕捉的,比如SIGKILL和SIGSTOP
sigaction函數
#
#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
其中,signum是信號代碼。 act指向sigaction結構的一個指針,用來指定對特定信號的處理。oldact保存原來對信號的處理操作。
成功返回0,錯誤返回-1.
5.信號集的操作:
信號集用sigset_t類型表示,在linux系統上有一組函數專用於對信號集進行操作:
#include <signal.h>
int sigemptyset(sigset_t *set);//清空信號集
int sigfillset(sigset_t *set);//將所有的信號加入信號集
int sigaddset(sigset_t *set, int signum);//將指定的信號加入信號集中
int sigdelset(sigset_t *set, int signum); //將指定的信號從信號集中去除
int sigismember(const sigset_t *set, int signum);//判斷一個指定的信號是否在信號集中,返回1表示在,0表示不再,-1表示出錯。
信號集中的信號並不是真正可以處理的信號,只有當信號處於非阻塞狀態纔可以使用。
set指向要操作的信號集,signum代表一個指定的信號
在調用sigaction函數時,可以設置信號處理時要屏蔽的信號。
實際上,在代碼中也可以直接設置或獲取進程的信號掩碼,如下:
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
參數說明:
how指定操作信號掩碼的方式,函數將根據how指定的方式,
設置當前進程的信號掩碼。
how的取值範圍:
SIG_BLOCK:將set參數指向的信號集中的信號加入到信號
掩碼中。
SIG_UNBLOCK:將set參數指向的信號集中的信號從信號掩
碼中刪除。
SIG_SETMASK:將set參數指向的信號集設置爲信號掩碼
oset是指信號屏蔽字,函數成功返回0,出錯返回-1.
6.信號的屏蔽方式:
方式一
sigset_t sigset;
sigemptyset(&sigset);
sigaddset(&sigset, SIGUSR1);
sigprocmask(SIG_BLOCK, &sigset, NULL);
方式二
sigset_t sigset;
// 得到當前的信號掩碼
sigprocmask(SIG_SETMASK, NULL, &sigset);
sigaddset(&sigset, SIGUSR1); // 將要屏蔽的信號加入
sigprocmask(SIG_SETMASK, &sigset, NULL);
7.解除信號屏蔽的方式
方式一
sigset_t sigset;
sigemptyset(&sigset);
sigaddset(&sigset, SIGUSR1);
sigprocmask(SIG_UNBLOCK, &sigset, NULL);
方式二
sigset_t sigset;
// 得到當前的信號掩碼
sigprocmask(SIG_SETMASK, NULL, &sigset);
sigdelset(&sigset, SIGUSR1); // 將要屏蔽的信號加入
sigprocmask(SIG_SETMASK, &sigset, NULL)
8.總結:在處理信號時,一般的操作過程是
(1)定義信號集
(2)設置信號屏蔽爲,使用sigprocmask函數
(3)定義信號處理函數
(4)測試信號sigpending函數(原型:int sigpending(sigset_t *set),成功返回0,出錯返回-1)
信號這邊內容比較多,實例參見linux代碼。
發佈了60 篇原創文章 · 獲贊 6 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章