Linux 訊號機制詳解(二)

2021-07-15 03:06:49 字數 2637 閱讀 1347

前面的詳解(一)已經詳細討論了訊號的種類,訊號產生的條件和訊號的阻塞訊號,基本上已經對訊號的處理方式有了一定的了解。今天我們繼續了解訊號的其他內容。

四。捕捉訊號

之前的訊號的處理中有提到過捕捉訊號,就是通過呼叫自定義的函式處理訊號

訊號捕捉舉例:

1. ⽤戶程式註冊了sigquit訊號的處理函式sighandler。

2. 當前正在執⾏main函式,這時發⽣中斷或異常切換到核心態。

3. 在中斷處理完畢後要返回⽤戶態的main函式之前檢查到有訊號sigquit遞達。

4. 核心決定返回⽤戶態後不是恢復main函式的上下⽂繼續執⾏,⽽是執⾏sighandler函

數,sighandler和main函式使⽤不同的堆疊空間,它們之間不存在調⽤和被調⽤的關係,

是 兩個獨⽴的控制流程。

5. sighandler函式返回後⾃動執⾏特殊的系統調⽤sigreturn再次進⼊核心態。

6. 如果沒有新的訊號要遞達,這次再返回⽤戶態就是恢復main函式的上下⽂繼續執⾏了。

下面是一些訊號處理的函式使用方法:

(一)sigaction函式

#include int sigaction(int signo, const struct sigaction *act, struct

sigaction *oact);

sigaction函式可以讀取和修改與指定訊號相關聯的處理動作。調⽤成功則返回0,出錯則返回- 1。 signo是指定訊號的編號。若act指標⾮空,則根據act修改該訊號的處理動作。

若oact指標⾮ 空,則通過oact傳出該訊號原來的處理動作。 

當某個訊號的處理函式被調⽤時,核心⾃動將當前訊號加⼊程序的訊號遮蔽字,當訊號處理函式返回時⾃動恢復原來的訊號遮蔽字,這樣就保證了在處理某個訊號時,如果這種訊號再次產⽣,那麼 它會被阻塞到當前處理結束為⽌。如果在調⽤訊號處理函式時,除了當前訊號被⾃動遮蔽之外,還希望⾃動遮蔽另外⼀些訊號,則⽤sa_mask欄位說明這些需要額外遮蔽的訊號,當訊號處理函式返回時⾃動恢復原來的訊號遮蔽字。

(二)pause函式

#include int pause(void);
pause函式使調⽤程序掛起直到有訊號遞達。如果訊號的處理動作是終⽌程序,則程序終⽌,pause函式沒有機會返回;如果訊號的處理動作是忽略,則程序繼續處於掛起狀態,pause不返回;如果訊號的處理動作是捕捉,則調⽤了訊號處理函式之後pause返回-1,errno設定為eintr, 所以pause只有出錯的返回值(想想以前還學過什麼函式只有出錯返回值?)。錯誤碼eintr表 ⽰「被訊號中斷」。

(三)可重入函式

當捕捉到訊號時,不論程序的主控制流程當前執⾏到哪⼉,都會先跳到訊號處理函式中執⾏,從訊號處理函式返回後再繼續執⾏主控制流程。訊號處理函式是⼀個單獨的控制流程,因為它和主控制流程是非同步的,⼆者不存在調⽤和被調⽤的關係,並且使⽤不同的堆疊空間。引⼊了訊號處理函式使得⼀個程序具有多個控制流程,如果這些控制流程訪問相同的全域性資源(全域性變數、硬體資源等),就有可能出現衝突。

如圖:

main函式調⽤insert函式向⼀個鍊錶head中插⼊節點node1,插⼊操作分為兩步,剛做完第⼀步的 時候,因為硬體中斷使程序切換到核心,再次回⽤戶態之前檢查到有訊號待處理,於

是切換 到sighandler函式,sighandler也調⽤insert函式向同⼀個鍊錶head中插⼊節點node2,插⼊操作的 兩步都做完之後從sighandler返回核心態,再次回到⽤戶態就從main函式調⽤的insert函式中繼續 往下執⾏,先前做第⼀步之後被打斷,現在繼續做完第⼆步。結果是,main函式和sighandler先後 向鍊錶中插⼊兩個節點,⽽最後只有⼀個節點真正插⼊鍊錶中了。

像這樣,insert函式被不同的控制流程調⽤,有可能在第⼀次調⽤還沒返回時就再次進⼊該函 數,這稱為重⼊,insert函式訪問⼀個全域性鍊錶,有可能因為重⼊⽽造成錯亂,像這樣的函式稱為 不可重⼊函式,反之,如果⼀個函式只訪問⾃⼰的區域性變數或引數,則稱為可重⼊(reentrant) 函式。

(四)sig_atomic_t型別與volatile限定符

在上⾯的例⼦中,main和sighandler都調⽤insert函式則有可能出現鍊錶的錯亂,其根本原因在 於,對全域性鍊錶的插⼊操作要分兩步完成,不是⼀個原⼦操作,假如這兩步操作必定會⼀起做完, 中間不可能被打斷,就不會出現錯亂了。

對於程式中存在多個執⾏流程訪問同⼀全域性變數的情況,volatile限定符是必要的,此外,雖然程 序只有單⼀的執⾏流程,但是變數屬於以下情況之⼀的,也需要volatile限定:

1. 變數的記憶體單元中的資料不需要寫操作就可以⾃⼰發⽣變化,每次讀上來的值都可能不⼀樣

2. 即使多次向變數的記憶體單元中寫資料,只寫不讀,也並不是在做⽆⽤功,⽽是有特殊意義的什麼樣的記憶體單元會具有這樣的特性呢?肯定不是普通的記憶體,⽽是對映到記憶體位址空間的硬體暫存器,例如串⼜的接收暫存器屬於上述第⼀種情況,⽽傳送暫存器屬於上述第⼆種情況。sig_atomic_t型別的變數應該總是加上volatile限定符,因為要使⽤sig_atomic_t型別的理由也正 是要加volatile限定符的理由。

Linux 訊號機制 (二)

推薦一篇好文章 文章二 程序即將從核心態返回使用者態的時候,才會處理訊號 執行訊號處理函式 使用者程序什麼時候從核心態返回使用者態呢?系統呼叫 使用者主動進入核心 中斷 使用者程序被動進入核心 被排程執行 使用者從等待執行,變為正在執行 訊號處理函式時使用者態函式,但是需要在核心態的條件下執行。所以...

Linux 信 號 機 制

前面介紹了訊號的基本概念,在這一節中,我們將介紹核心如何實現訊號機制。即核心如何向乙個程序傳送訊號 程序如何接收乙個訊號 程序怎樣控制自己對訊號的反應 核心在什麼時機處理和怎樣處理程序收到的訊號。還要介紹一下setjmp和longjmp在訊號中起到的作用。1 核心對訊號的基本處理方法 核心給乙個程序...

linux 訊號機制

本文旨在弄懂linux中的訊號工作原理 kill l 命令可以檢視linux下所有訊號 2.1 使用者在終端按下某些鍵時,終端驅動程式會傳送訊號給前台程序 例如ctrl c產生sigint訊號,ctrl 產生sigquit訊號,ctrl z產生sigtstp訊號 2.2 硬體異常產生訊號,這些條件由...