Linux 訊號的理解以及訊號集處理函式的使用

2021-07-31 22:48:00 字數 4070 閱讀 5172

通過終端按鍵產生。 如ctrl+c 終止程序

通過系統函式向程序傳送訊號。如kill()函式,給指定程序傳送訊號。

有軟體條件產生訊號。如alarm()函式,設定乙個鬧鐘訊號。

硬體異常。如記憶體越界,除0異常。

// 傳送訊號給程序

#include

#include

int kill(pid_t pid, int sig);

先寫乙個死迴圈程式test並在後台跑起來

#include 

int main()

這是利用kill 函式實現的mykill:

#include 

#include

#include

#include

#include

int main( int argc, char** argv)

// 獲取pid

然後執行jobs ,可以看到後台作業test已經存在。

此時執行mykill 並 輸入test程式的pid

第一次執行jobs發現test狀態已經從running 變為 killed,再次執行就會看到已經被乾掉。

#include 

unsigned

int alarm( unsigned

int seconds);

alarm函式可以設定乙個鬧鐘,告訴核心在seconds秒之後給當前程序傳送sigalrm訊號,該訊號的預設處理動作是終止當前程序。 函式返回值是0,或者是上次鬧鐘剩餘時間。

比如先設定鬧鐘10s, 然後呼叫alarm(0) 表示取消鬧鐘, 如果返回0, 說明鬧鐘是在10後響的,如果大於0,則說明該鬧鐘提前響了。

下面是乙個使用alarm函式的小栗子, 測試一秒對可以執行多少次++,並在每次++後列印值:

#include 

#include

int main()

return

0;}

乙個程序在收到乙個訊號後,有三種方式處理方式:

1. 忽略此訊號

2. 執行預設動作

3. 提供乙個訊號處理函式,要求核心在處理該訊號時切換到使用者態,這種方式也叫捕捉訊號。

乙個捕捉訊號的小例子:

在**中,我們捕捉了sigint 訊號,也就是2號訊號(kill -l 可以看到全部訊號列表), 作業系統收到ctrl + c ,傳送該訊號給當前前台作業。發現執行後它每個一秒列印一條「你殺不掉我 hhh」, 當我們按ctrl+c 也無濟於事,因為我們為該程序提供了2號訊號的自定義函式「訊號被我吃掉了」。最後我們只能以ctrl + \乾掉它。

以上討論了訊號產生的各種原因,而實際執行訊號的動作稱為遞達(delivery),訊號從產生到遞達之間的狀態,稱為訊號未決(pending)。程序可以設定阻塞(block)某個訊號。

如果乙個程序阻塞了某個訊號,那麼在它收到被阻塞的訊號時,該訊號會處於未決狀態。直到對該訊號解除阻塞,才會執行抵達的動作。

需要注意的是,訊號阻塞和訊號忽略是不同的。訊號忽略是在該訊號被遞達後執行的動作,而阻塞說明該該訊號在解除阻塞之前不可能遞達。

我們知道在系統中執行的每乙個程序都有乙個 pcb, 而乙個程序對應的訊號資訊也會被作業系統記錄在該程序的 pcb 上。在task_struct 結構體會有對應的字段來記錄程序當前是否有待處理的訊號, 還有記錄當前要阻塞的訊號,以及訊號對應的處理函式。下面用一張圖來說明一下關係:

我們可以這樣理解:以看做在pcb中有三張**,分別是block表,pending表,handler表。

block表中1 表示該訊號被阻塞,當有訊號產生時不會被抵達,會處於未決狀態。

而pending表記錄未被處理的訊號。handler 對應每個訊號的處理方式,有預設和忽略,以及乙個函式指標,指向我們提供的處理函式。

通過上面的**可以發現,阻塞和未決狀態,每個訊號只需要對應乙個bit位即可解決1表示有效,0表示無效,所以系統為我們提供了訊號量集 sigset_t 來儲存阻塞和未決的狀態。

#include 

int sigemptyset(sigset_t *set);

int sigfillset(sigset_t *set);

以上兩個函式用來將訊號機全部置0或全部置1,在使用之前,務必呼叫對應初始化函式,讓訊號機處於確定狀態。

int sigaddset(sigset_t *set, int signum);

int sigdelset(sigset_t *set, int signum);

上面兩個函式用來給,指定訊號集,新增或刪除signum訊號。

int sigismember(sigset_t *set, int );
用來判斷訊號集中是否有該訊號,有則返回1,無返回0,執行失敗返回-1。

用來讀取或者更改程序的訊號遮蔽集。

int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
如果 set 為空 oldset 不為空, 則會將程序原來的訊號遮蔽集傳出;

如果 set 不為空,oldset 為空, 則會根據 how 引數的指示修改程序的訊號遮蔽集;

如果兩個指標都不為空, 則先備份原來的訊號遮蔽字到 oldset, 然後根據 how引數修改。

how引數有如下函式值含義

sig_block

將當前程序遮蔽字mask 增加我們希望通過引數set的遮蔽訊號,相當於mask與set執行按位或

sig_unblock

將當前程序遮蔽字mask 刪除我們希望通過引數set解除遮蔽的訊號

sig_setmask

設定當前訊號遮蔽字為set,相當於 mask = set

用來讀取當前程序的未決訊號集。

#include 

int sigpending(sigset_t *set);

下面運用上面介紹的訊號集函式寫乙個小實驗。

程式執行時,每秒列印一此未決狀態訊號集,初始全為0,當輸出ctrl-c時, 由於我們阻塞了sigint訊號, 會使該訊號處於未決狀態。

#include 

#include

// 列印訊號集

Linux 訊號的處理以及訊號集操作函式

首先來再次看看這張圖 從上圖來看,每個訊號只有乙個bit的未決標誌,非0即1,不記錄該訊號產生了多少次,阻塞標誌也是這樣表示的。因此,未決和阻塞標誌可以用相同的資料型別sigset t來儲存,sigset t稱為訊號集,這個型別可以表示每個訊號的 有效 或 無效 狀態,在阻塞訊號集中 有效 和 無效...

對訊號集與訊號量的理解

把他的程式重新改動一下,改為一次性為訊號集合申請兩個訊號量 變成 include include include include include define lock 1 define unlock 1 semophore operation lock unlock void semop01 in...

Linux下的訊號以及訊號的處理

阻塞訊號 訊號捕捉 訊號處理 根據生活中的經驗,訊號就是向乙個事物傳遞某些資訊,在shell下啟動乙個前台程序,使用者輸入ctrl c,這個過程就相當於是再給核心傳遞乙個程序取消的訊號。那麼使用者按下ctrl c後,發生了什麼?在鍵盤上按下ctrl c等來產生訊號 include intkill p...