生活中有許許多多的信號,能夠反映給人類,人類能夠產生相應的行爲。當我們使用鍵盤給計算機一個信號,計算機也會相應的產生一系列的行爲。在linux系統中,使用kill -l命令能夠查看系統中所有的信號如下:
其中:系統中總共有62中信號,前31種信號稱爲普通信號,後31中稱爲實時信號。當然在這裏主要討論的是前31種。
★信號產生
(1)信號產生的方式主要有以下4種:
①鍵盤:當一個進程正在運行時,可以從鍵盤上輸入ctrl+c使得進程終止,其實是鍵盤上輸入的信息被系統捕捉,系統向前臺進程發送2號信號(即就是SIGINT),進而將程序進行終止。
注:信號反映於前臺,後臺沒有作用,不能使得程序終止。向後臺反映使用&,如:./test &
例:
運行結果:
②命令:可以使用kill [信號] PID 這個命令。如:kill -2 10236
③函數:可以使用raise( )函數和abort( )函數。
int kill(pid_t pid, int signo);
int raise(int signo);
void abort(void);
④軟件條件:利用alarm函數,設置鬧鐘,等到一定時間後,向當前進程發送SIGALRM信號。
例:
運行結果:
★信號處理
(1)進程的處理方式有3種:
①忽略此信號
②執行默認的處理方式
③提供自定義的信號處理函數,將其稱爲捕捉
注:①絕大部分進程接收到信號不會立即進行處理的,會先將其保存在自己的PCB中,等到合適的時候進行處理。
②信號的默認處理方式是終止進程。
(2)相關概念:
信號未決:將信號產生到信號執行之前這段時間稱爲信號未決。
阻塞:進程可以阻塞某個信號,被阻塞後的信號只有解除阻塞,才能夠進行執行。
信號遞達:將信號的處理動作稱爲遞達。
注:信號的產生和阻塞是沒有關係的,所以即使沒有產生信號,系統也可以將此種信號進行阻塞。
在linux系統中,只記錄產生了哪種信號,並不會將信號的次數進行記錄,所以只需要將31種信號用4個字節(32)位來進行表示,這些信息都會保存在進程的PCB中,存在block、pending、handler。block表示每個信號是否被阻塞,0表示沒有阻塞,1表示被阻塞。pending表示進程中是否存在某種信號,0表示不存在,1表示存在,。handler表示存儲方式,SIG_DFL表示執行默認的處理方式,SIG_IGN表示忽略此信號。
未決和阻塞都可以使用相同的數據類型sigset_t來存儲,可以表示有效、無效兩種狀態,分別稱爲‘信號屏蔽字’和‘pending信號集’。
注:信號集中不能使用位運算來進行操作。
★信號集操作函數
頭文件:#include <signal.h>
函數:int sigemptyset(sigset_t* set); //將信號集中所有的數據置爲無效
int sigfillset(sigset_t* set); //將信號集中所有的數據置爲有效
int sigaddset(sigset_t* set, int signo); //在信號集中增加哪個信號
int sigdelset(sigset_t* set, int signo); //刪除信號集中的某個信號
int sigismember(const sigset_t* set, int signo); //判斷某個信號是否在信號集中
int sigprocmask(int how, const sigset_t* set, sigset_t* oset); //獲取或更改進程的信號屏蔽字
int sigpending(sigset_t* set); //獲取當前進程的未決信號集
例:
運行結果:
★捕捉信號
在上面提到信號產生後,進程不是立即對其處理,而是先將其保存在自己的PCB中,等到合適的時候來處理。其實,合適的時候就是:在由於某些異常原因,進程從用戶態切換到內核態,返回之前需要對信號進行檢查、處理(即就是內核態到用戶態切換時)。
int sigaction(int signo, const struct sigaction* act, struct sigaction* oact); //讀取和修改指定信號的處理動作。
int pause(void); //將進程掛起,直到有信號遞達
例:使用上面兩個函數完成sleep函數
運行結果:
注:這裏不考慮多線程的切換問題。
本文出自 “無心的執着” 博客,謝絕轉載!