信號是一種進(jìn)程間的通信機(jī)制,它給應(yīng)用程序提供一種異步的軟件中斷,是應(yīng)用程序有機(jī)會接受其他程序活終端發(fā)送的命令 (即信號)。應(yīng)用程序收到信號后,有三種處理方式:忽略,默認(rèn),捕捉。該進(jìn)程收到一個信號后,會檢查對該信號的處理機(jī)制。如果是 SIG_IGN,就會忽略該信號;如果是 SIG_DFT,則會采用系統(tǒng)默認(rèn)的處理動作,通常是終止進(jìn)程或忽略該信號;如果給該信號指定了一個處理函數(shù) (捕捉),則會中斷當(dāng)前進(jìn)程正在執(zhí)行的任務(wù),轉(zhuǎn)而去執(zhí)行該信號的處理函數(shù),返回后在繼續(xù)執(zhí)行被中斷的任務(wù)。
在有些情況下,我們不希望自己的 shell 腳本在運(yùn)行時刻被中斷,比如說我們寫的 shell 腳本設(shè)為某一用戶的默認(rèn) shell,使這一用戶進(jìn)入系統(tǒng)后只能做某一項(xiàng)工作,如數(shù)據(jù)庫備份,我們可不希望用戶使用 Ctrl+C 鍵便能進(jìn)入到 shell 狀態(tài),做我們不希望看到的事情,這便用到了信號處理。
以下是一些常見的信號:
```信號名稱 信號數(shù) 說明 SIGHUP 1 本信號在用戶終端連接 (正?;蚍钦? 結(jié)束時發(fā)出,通常是在終端的控制進(jìn)程結(jié)束時,通知同一 session 內(nèi)的各個作業(yè),這時它們與控制終端不再關(guān)聯(lián)。登錄 Linux 時,系統(tǒng)會分配給登錄用戶一個終端 (Session)。在這個終端運(yùn)行的所有程序,包括前臺進(jìn)程組和后臺進(jìn)程組,一般都屬于這個 Session。當(dāng)用戶退出 Linux 登錄時,前臺進(jìn)程組和后臺有對終端輸出的進(jìn)程將會收到 SIGHUP 信號。這個信號的默認(rèn)操作為終止進(jìn)程,因此前臺進(jìn)程組和后臺有終端輸出的進(jìn)程就會中止。對于與終端脫離關(guān)系的守護(hù)進(jìn)程,這個信號用于通知它重新讀取配置文件。 SIGINT 2 程序終止 (interrupt) 信號,在用戶鍵入 INTR 字符 (通常是 Ctrl+C) 時發(fā)出 SIGQUIT 3 和 SIGINT 類似,但由 QUIT 字符 (通常是 Ctrl /) 來控制。進(jìn)程在因收到 SIGQUIT 退出時會產(chǎn)生 core 文件,在這個意義上類似于一個程序錯誤信號。 SIGFPE 8 在發(fā)生致命的算術(shù)運(yùn)算錯誤時發(fā)出。不僅包括浮點(diǎn)運(yùn)算錯誤,還包括溢出及除數(shù)為 0 等其它所有的算術(shù)的錯誤。 SIGKILL 9 用來立即結(jié)束程序的運(yùn)行。本信號不能被阻塞,處理和忽略。 SIGALRM 14 時鐘定時信號,計算的是實(shí)際的時間或時鐘時間。alarm 函數(shù)使用該信號 SIGTERM 15 程序結(jié)束 (terminate) 信號,與 SIGKILL 不同的是該信號可以被阻塞和處理。通常用來要求程序自己正常退出。shell 命令 kill 缺省產(chǎn)生這個信號。
**捕獲信號**
當(dāng)按下了 `Ctrl+C` 鍵或 `break` 鍵在終端一個 shell 程序的執(zhí)行過程中,正常程序?qū)⒘⒓唇K止,并返回命令提示符。這可能并使總是可取的,例如,你可能最終留下了一堆臨時文件,將不會清理。
捕獲這些信號是很容易的,`trap` 命令的語法如下:
`\#trap commands signals`
這里的 `commands` 可以是任何有效的 linux 命令,或一個用戶定義的函數(shù),信號可以是任意數(shù)量的信號,你想來捕獲的列表。
- trap 捕捉到信號之后,可以有三種反應(yīng)方式:
1. 執(zhí)行一段程序來處理這一信號
2. 接受信號的默認(rèn)操作
3. 忽視這一信號
- trap 對上面三種方式提供了三種基本形式:
- 第一種形式的 `trap` 命令在 shell 接收到 signal list 清單中數(shù)值相同的信號時,將執(zhí)行雙引號中的命令串。
`trap 'commands' signal-list`
`trap "commands" signal-list`
- 為了恢復(fù)信號的默認(rèn)操作,使用第二種形式的 trap 命令
`trap signal-list`
- 第三種形式的 trap 命令允許忽視信號
`trap " " signal-list`
注意:
1. 對信號 11(段違例) 不能捕捉,因?yàn)?shell 本身需要捕捉該信號去進(jìn)行內(nèi)存的轉(zhuǎn)儲。
2. 在 trap 中可以定義對信號 0 的處理 (實(shí)際上沒有這個信號),shell 程序在其終止 (如執(zhí)行 exit 語句) 時發(fā)出該信號。
3. 在捕捉到 `signal-list` 中指定的信號并執(zhí)行完相應(yīng)的命令之后,如果這些命令沒有將 shell 程序終止的話,shell 程序?qū)⒗^續(xù)執(zhí)行收到信號時所執(zhí)行的命令后面的命令,這樣將很容易導(dǎo)致 shell 程序無法終止。另外,在 `trap` 語句中,單引號和雙引號是不同的,當(dāng) shell 程序第一次碰到 `trap` 語句時,將把 `commands` 中的命令掃描一遍。此時若 `commands` 是用單引號括起來的話,那么 shell 不會對 `commands` 中的變量和命令進(jìn)行替換,否則 `commands` 中的變量和命令將用當(dāng)時具體的值來替換。
**trap 命令用于指定在接收到信號后將要采取的動作。常見的用途是在腳本程序被中斷時完成清理工作。**
測試案例:
按照用戶的要求,我們需要屏蔽的是 `HUP INT QUIT TSTP` 幾個信號。所以,可以運(yùn)行:
`\#trap “”HUP INT QUIT TSTP`
這個時候,可以試試打開一個持續(xù)的命令,然后中斷其運(yùn)行,例如:
`\#tail -f /var/log/messages`
接著,試試用 `Ctrl+C` 或 `Ctrl+\` 來中斷試試,該進(jìn)程是不會退出的。
**恢復(fù)信號**
如果想恢復(fù)的話,可以用 `Ctrl+Z` 吧進(jìn)程放到后臺,然后運(yùn)行:
`\#trap :HUP INT QUIT TSTP`
然后,用 `#ps -ef` 看看其 PID 號,`bg 1` 讓程序繼續(xù)運(yùn)行,最后用 kill 殺掉即可。
**其他**
可以試試運(yùn)行:
`\#trap “echo ‘hello world’” HUP INT QUIT TSTP`
這樣,當(dāng)你運(yùn)行 `Ctrl+C` 等中斷時,會自動運(yùn)行 `echo` 命令,結(jié)果就是實(shí)現(xiàn) helloworld 字符串。
**引用**
`\#tail -f /var/log/messages`
注意,這方式并不能屏蔽中斷,按下 `Ctrl+C` 鍵仍然會退出程序,僅會再運(yùn)行一個額外的命令而已。