首頁 > 軟體

shell指令碼進階之信號的捕捉trap

2020-06-16 16:31:55

shell指令碼之信號的捕捉

?trap,翻譯過來就是陷阱的意思,shell指令碼中的陷阱是專門用來捕捉信號的。啥信號呢?比如經常使用的kill -9,kill -15,CTRL+C等都屬於信號

1、檢視所有可用的信號

trap -l或kill -l即可

[root@linux1 ~]# kill -l
63) SIGRTMAX-1  64) SIGRTMAX   
[root@linux1 ~]# trap -l
 1) SIGHUP  2) SIGINT  3) SIGQUIT  4) SIGILL  5) SIGTRAP
 6) SIGABRT  7) SIGBUS  8) SIGFPE  9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
...... 

2、常見的信號如下:

Signal    Value    Comment
─────────────────────────────
SIGHUP        1      終止進程,特別是終端退出時,此終端內的進程都將被終止
SIGINT        2      中斷進程,幾乎等同於sigterm,會盡可能的釋放執行clean-up,釋放資源,儲存狀態等(CTRL+C)
SIGQUIT      3      從鍵盤發出殺死(終止)進程的信號

SIGKILL      9      強制殺死進程,該信號不可被捕捉和忽略,進程收到該信號後不會執行任何clean-up行為,所以資源不會釋放,狀態不會儲存
SIGTERM      15      殺死(終止)進程,幾乎等同於sigint信號,會盡可能的釋放執行clean-up,釋放資源,儲存狀態等

SIGSTOP      19      該信號是不可被捕捉和忽略的進程停止資訊,收到信號後會進入stopped狀態
SIGTSTP      20      該信號是可被忽略的進程停止信號(CTRL+Z)

真正的信號名字不是SIGXXX,而是去除SIG後的單詞,每個信號還有對應的代號

比如向PID為12345的進程發起1信號

kill -1 12345
kill -HUB 12345
kill -SIGHUB 12345

3、trap的選項

trap -l列出當前系統支援的信號列表,上面已經使用過,根kill -l一樣

trap -p等價於trap,檢視shell已經佈置好的陷阱

可以看到shell預設有三個陷阱,表示忽略20,21,22信號

[root@linux1 ~]# trap
trap -- '' SIGTSTP
trap -- '' SIGTTIN
trap -- '' SIGTTOU

4、陷阱捕捉到信號後幹嘛
•忽略信號
•捕捉到信號後做相應的處理。主要是清理一些指令碼建立的臨時檔案,然後退出。

5、設定一個可以忽略CTRL+C和15信號的陷阱

CTRL信號對應的是SIGINT 15信號對應的是SIGTERM

[root@linux1 ~]# trap '' SIGINT SIGTERM
[root@linux1 ~]# trap
trap -- '' SIGINT
trap -- '' SIGTERM
trap -- '' SIGTSTP
trap -- '' SIGTTIN
trap -- '' SIGTTOU

這樣,當前shell就不能被kill -15殺死

6、設定一個陷阱,捕捉到-15信號時,就列印“我抓到你啦~”

[root@linux1 ~]# trap 'echo "我抓到你啦~"' TERM
[root@linux1 ~]# trap
trap -- '' SIGINT
trap -- 'echo "我抓到你啦~"' SIGTERM
trap -- '' SIGTSTP
trap -- '' SIGTTIN
trap -- '' SIGTTOU

效果,當我對當前bash發起kill -15信號時就列印出來了

[root@linux1 ~]# echo $$
8827
[root@linux1 ~]# kill -15 8827
我抓到你啦~
[root@linux1 ~]# kill -15 8827
我抓到你啦~
[root@linux1 ~]# kill -15 8827
我抓到你啦~

7、在指令碼中設定一個能忽略CTRL+C和CTRL+Z信號的指令碼

CTRL+C是2信號,即SIGINT

CTRL+Z是20信號,即SIGTSTP

指令碼:

指令碼沉睡10s,然後列印success,指令碼忽略INT和TSTP信號

[root@linux1 ~]# cat trap.sh
#!/bin/bash
trap '' SIGINT SIGTSTP
sleep 10
echo success

效果:

<font color=red>CTRL+C也不能阻止我睡覺</font>

[root@linux1 ~]# bash trap.sh
^C^C^Z^Z^C^C^Z^Zccc^Z^Z^Z^C^C^C

success

8、布置一個當指令碼被終端時能清理垃圾並立即退出指令碼的陷阱

指令碼如下:

[root@linux1 ~]# cat trap1.sh
#!/bin/bash

trap 'echo trap handing...;rm -rf /tmp/$BASHPID;echo TEMP files cleaned;exit' SIGINT SIGTERM SIGQUIT SIGHUP
mkdir -p /tmp/$$/
touch /tmp/$$/{a..c}.txt
sleep 10
echo first sleep success
sleep 10
echo second sleep success

這樣,指令碼除了SIGKILL信號(kill -9),總能清理掉臨時垃圾

效果

剛開始一直不能終止,後來執行了下trap發現前面shell自己設定了一個忽略CTRL+C的陷阱,退出shell重進即可

[root@linux1 ~]# bash trap1.sh
^Ctrap handing...
TEMP files cleaned

9、陷阱的守護物件

陷阱的守護物件是shell進程本身,不會守護shell環境內的子進程。但如果是信號忽略型陷阱,則會守護整個shell行程群組使其忽略給定信號。

[root@linux1 ~]# cat trap2.sh
#!/bin/bash
trap 'echo trap_handle_time: $(date +"%F %T")' SIGINT SIGTERM
echo time_start: $(date +"%F %T")
sleep 10
echo time_end1: $(date +"%F %T")
sleep 10
echo time_end2: $(date +"%F %T")

#執行指令碼後,新開終端使用kill -15殺死它
[root@linux1 ~]# killall -s SIGTERM trap2.sh

#檢視輸出情況
[root@linux1 ~]# ./trap2.sh
time_start: 2019-08-27 10:43:48
trap_handle_time: 2019-08-27 10:43:58
time_end1: 2019-08-27 10:43:58
time_end2: 2019-08-27 10:44:08

? 可以發現,kill執行完後,螢幕沒有立即列印trap_handle,而是等sleep 10執行完後才列印的。sleep進程都被忽略型trap守護了

? 只要是向shell進程傳送的信號,都會等待當前正在執行的命令結束後才處理信號,然後繼續指令碼向下執行。(實際上,只有當shell指令碼中正在執行的操作是信號安全的系統呼叫時,才會出現信號無法中斷進程的情況,而在shell下的各種命令,我們是沒法直接知道哪些命令中正在執行的系統呼叫是系統呼叫的)。

但sleep命令發起的sleep()呼叫,是一個信號安全的,所以上面指令碼中執行sleep的過程中,信號不會直接中斷它們的執行,而是等待它執行完之後再執行信號處理命令。

10、如果shell中針對某信號設定了陷阱,則該shell進程接收到該信號時,<font color=red>會等待其內正在執行的命令結束才開始處理陷阱</font>

11、CTRL+C和SIGINT不是等價的。當某一時刻按下CTRL+C,它是在向整個當前執行的行程群組傳送SIGINT信號。對shell指令碼來說,SIGINT不僅傳送給shell指令碼進程,還傳送給指令碼中當前正在執行的進程

[root@linux1 ~]# cat trap2.sh
#!/bin/bash
trap 'echo trap_handle_time: $(date +"%F %T")' SIGINT SIGTERM
echo time_start: $(date +"%F %T")
sleep 10
echo time_end1: $(date +"%F %T")
sleep 10
echo time_end2: $(date +"%F %T")

#執行指令碼後,立馬CTRL+C
[root@linux1 ~]# bash trap2.sh
time_start: 2019-08-27 10:20:53
^Ctrap_handle_time: 2019-08-27 10:20:54
time_end1: 2019-08-27 10:20:54
time_end2: 2019-08-27 10:21:04

可以發現,CTRL+C後,不僅trap進行處理了,sleep也立馬結束了;說明CTRL+C不僅讓指令碼進程收到了SIGINT信號,也讓當前進程收到了SIGINT信號

有點難理解,以後再來看。


IT145.com E-mail:sddin#qq.com