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