1. 程式人生 > 其它 >學習筆記9(必做)

學習筆記9(必做)

第6章 訊號和訊號處理

摘要

本章講述了訊號和訊號處理;介紹了訊號和中斷的統一處理,有助於從正確的角度看待訊號;將訊號視為程序中斷,將程序從正常執行轉移到訊號處理;解釋了訊號的來源,包括來自硬體、異常和其他程序的訊號;然後舉例說明了訊號在Unix/Linux中的常見用法;詳細解釋了Unix/Linux中的訊號處理,包括訊號型別、訊號向量位、訊號掩碼位、程序PROC結構體中的訊號處理程式以及訊號處理步驟;用示例展示瞭如何安裝訊號捕捉器來處理程式異常,如使用者模式下的段錯誤;還討論了將訊號用作程序間通訊(IPC)機制的適用性。讀者可藉助該程式設計專案,使用訊號和管道來實現用於程序交換資訊的程序間通訊機制。

6.1訊號和中斷

“中斷”是從1/0裝置或協處理器傳送到CPU的外部請求,它將CPU從正常執行轉移到中斷處理。與傳送給CPU的中斷請求一樣,“訊號”是傳送給程序的請求,將程序從正常執行轉移到中斷處理。
(1)首先,我們將程序的概念概括為:一個“程序”(引號中)就是一系列活動。廣義的 “程序”包括
・從事日常事務的人。
・在使用者模式或核心模式下執行的Unix/Linux程序。
・執行機器指令的CPU。
(2)“中斷”是傳送給“程序”的事件,它將“程序”從正常活動轉移到其他活動,稱 為“中斷處理”。“程序”可在完成“中斷”處理後恢復正常活動。
(3)“中斷” 一詞可應用於任何“程序”,並不僅限於計算機中的CPU。例如,我們可能會提到以下幾種“中斷”。
(3)當我在辦公室讀書、評分、做白日夢時,可能會發生一些真實事件,比如:

真實事件 ID 動作函式
大樓著火了 1 馬上離開
電話響了 2 拿起電話和打電話的人聊天
有人敲門 3 回答請進(或假裝不在)
切到自己的手指 4 貼創口貼

所有這些事件都叫作人員中斷。因為他們把人從正常活動轉向“應對或處理中斷”。處理完中斷後,此人可以繼續此前的活動(如果這個人還活著而且仍然記得自己之前的活動)。
每個中斷都分配有一個唯一的ID識別號,並有一個預先安裝的動作函式,人可在收到中斷請求時“執行”動作函式。根據來源,中斷可分為三類:

  • 來自硬體的中斷:大樓著火,鬧鐘響了等。
  • 來自其他人的中斷:電話響了,有人敲門等。
  • 自己造成的中斷:切到手指,吃得太多等。

按照緊急程度,中斷可分為以下幾類:

  • 不可遮蔽(NMI):大樓著火!
  • 可遮蔽:有人敲門等。

人員的每個動作函式都是通過本能或經驗實現的。由於人員中斷的種類太多,所以不能在上表中全部列出,但是思路應該清晰。
(3).2程序中斷。
這類中斷是傳送給程序的中斷。當某程序正在執行時,可能會收到來自3個不同來源的中斷:

  • 來自硬體的中斷:終端、間隔定時器的“Ctrl+C”組合鍵等。
  • 來自其他程序的中斷:kill(pid, SIG#)、death_of_child 等。
  • 自己造成的中斷:除以0、無效地址等。

每個程序中斷都被轉換為一個唯一ID號,傳送給程序。與多種類的人員中斷不同,我們始終可限制在一個程序中的中斷的數量。Unix/Linux中的程序中斷稱為訊號,編號為1到31。程序的PROC結構體中有對應每個訊號的動作函式,程序可在收到訊號後執行該動作函式。與人員類似,程序也可遮蔽某些型別的訊號,以推遲處理。必要時,程序還可能會修改訊號動作函式。

(3) .4程序的陷阱錯誤。
程序可能會自己造成中斷。這些中斷是由被CPU識別為異常的錯誤引起的,例如除以0、無效地址、非法指令、越權等。當程序遇到異常時,它會陷入作業系統核心,將陷阱原因轉換為訊號編號,並將訊號傳送給自己。如果在使用者模式下發生異常,則程序的預設操作是終止,並使用一個可選的記憶體轉儲進行除錯。我們會在後面學習到,程序可以用訊號捕捉 器代替預設動作函式,允許它在使用者模式下處理訊號。如果在核心模式下發生陷阱,原因一定是硬體錯誤,或者很可能是核心程式碼中的漏洞,在這種情況下,核心無法處理。在Unix/Linux中,核心只打印一條PANIC錯誤訊息,然後就停止了。希望在下一個核心版本中可以跟蹤並修復這個問題。

6.2 Unix/Linux訊號示例

(1)按"Ctrl+C”組合鍵通常會導致當前執行的程序終止。原因如下。“Ctrl+C”組合鍵會生成一個鍵盤硬體中斷。鍵盤中斷處理程式將"Ctrl+C”組合鍵轉換為SIGINT (2)訊號,傳送給終端上的所有程序,並喚醒等待鍵盤輸入的程序。在核心模式下,每個程序都要檢查和處理未完成的訊號。程序對大多數訊號的預設操作是呼叫核心的kexit(exitValue)函式來終止。在Linux中,exitValue的低位位元組是導致程序終止的訊號編號。
(2)使用者可使用nohup a.out &命令在後臺執行一個程式。即使在使用者退出後,程序仍將繼續執行。nohup命令會使sh像往常一樣復刻子程序來執行程式,但是子程序會忽略S1GHUP(1)訊號。當用戶退出時,sh會向與終端有關的所有程序傳送一個S1GHUP訊號。後臺程序在接收到這一訊號後,會忽略它並繼續執行。為防止後臺程序使用終端進行I/O, 後臺程序通常會斷開與終端的連線(通過將其檔案描述符0、1、2重定向到/dev/null),使 其完全不受任何面向終端訊號的影響。
(3)也許幾天後,使用者再次登入時會發現(通過ps-u LTD)後臺程序仍在執行。使用者可以使用sh命令

kill pid (or kill -s 9 pid)

殺死該程序。方法如下。執行殺死的程序向pid標識的目標程序傳送一個SIGTERM(15)訊號,請求它死亡。目標程序將會遵從請求並終止。如果程序選擇忽略SIGTERM訊號,它可能拒絕死亡。在這種情況下,我們可以使用kill -s 9 pid,肯定能殺死它。因為程序不能修改對9號訊號的動作。讀者可能會問,為什麼是9號訊號呢?在最初的Unix中,只有9個訊號。9號訊號被保留為終止程序的終極手段。雖然後來的Unix/Linux系統將訊號編號擴充套件到了31,但是訊號編號9的含義仍然保留了下來。

6.3 Unix/Linux中的訊號處理

6.3.1 訊號型別

Unix/Linux支援31種不同的訊號,每種訊號在signal.h檔案中都有定義。

#define	SIGHUP	1
#define	SIGINT	2
#define	SIGQUIT	3
#define	SIGILL	4
#define	SIGTRAP	5
#define	SIGABRT	6
#define	SIGIOT	6
#define	SIGBUS	7
#define	SIGFPE	8
#define	SIGKILL	9
#define	SIGUSR1	10
#define	SIGSEGV	11
#define	SIGUSR2	12
#define	SIGPIPE	13
#define	SIGALRM	14
#define	SIGTERM	15
#define	SIGSTKFLT	16
#define	SIGCHLD	17
#define	SIGCONT	18
#define	SIGSTOP	19
#define	SIGTSTP	20
#define	STGTTTN	21
#define	SIGTTOU	22
#define	SIGURG	23
#define	SIGXCPU	24
#define	SIGXFSZ	25
#define	SIGVTALRM	26
#define	SIGPROF	27
#define	SIGWINCH	28
#define	SIGPOLL	29
#define	SIGPWR	30
#define	SIGSYS	31

每種訊號都有一個符號名,如 SIGHUP ( 1 )、SIGEMT ( 2 )、SIGKILL ( 9 )、S1GSEGV (11)等。

6.3.2 訊號的來源

  • 來自硬體中斷的訊號:在程序執行過程中,一些硬體中斷被轉換為訊號傳送給程序。 硬體訊號示例是
    • 中斷鍵(Ctrl+C),它產生一個SIGINT ( 2 )訊號。
    • 間隔定時器,當它的時間到期時,會生成一個SIGALRM ( 14 ) .SIGVTALRM ( 26 ) 或 SIGPROF ( 27 )訊號。
    • 其他硬體錯誤,如匯流排錯誤、IO陷阱等。
  • 來自異常的訊號:當用戶模式下的程序遇到異常時,會陷入核心模式,生成一個信 號,併發送給自己。常見的陷阱訊號有SIGFPE ( 8 ),表示浮點異常(除以0),最常 見也是最可怕的是SIGSEGV (11),表示段錯誤,等等。
  • 來自其他程序的訊號:程序可使用kill(pid, sig)系統呼叫向pid標識的目標程序傳送 訊號。讀者可以嘗試以下實驗。在Linux下,執行簡單的C程式
main()( while(1); }

使程序無限迴圈。從另一個(X-window)終端,使用ps-u查詢迴圈程序pid。然後 輸入sh命令

kill -s 11 pid

迴圈程序會因為段錯誤而死亡。讀者可能會問:這怎麼可能呢?所有程序都在一個 While(l)迴圈中執行,它是如何產生段錯誤的呢?答案是:這並不重要。當某程序被某個訊號終止時,它的exitValue就包含這個訊號編號。父程序sh只是將死亡子程序的訊號編號轉換為一個錯誤字串,不管它是什麼。

6.3.3 程序PROC結構體中的訊號

每個程序PROC都有一個32位向量,用來記錄傳送給程序的訊號。在位向量中,每一 位(0位除外)代表一個訊號編號。此外,它還有一個訊號MASK位向量,用來遮蔽相應的 訊號。可使用一系列系統呼叫,如sigmasks sigsetmask, siggetmask. sigblock等設定、清
除和檢查MASK位向量。待處理訊號只在未被遮蔽的情況下才有效。這樣可以讓程序延遲 處理被遮蔽的訊號,類似於CPU遮蔽某些中斷。

6.3.4 訊號處理函式

每個程序PROC都有一個訊號處理陣列int sig[32]o sig[32]陣列的每個條目都指定了如何處理相應的訊號,其中0表示DEFault (預設),1表示IGNore (忽略),其他非零值表示 使用者模式下預先安裝的訊號捕捉(處理)函式。圖6.1給岀了訊號位向量、遮蔽位向量和訊號處理函式。

———————————————————————————————————————————————————————————————— 轉載麻煩附上本文連結和本宣告,感謝! 博主<葉家星>部落格園連結如下:https://www.cnblogs.com/yejiaxing-01/