linux中脚本扑捉(trap)信号问题
撲捉ctrl+c信號:
1 #!/bin/bash
2 trap "trap" 2;
3 function trap()
4 {
5 echo "You press Ctrl+C.";
6 echo "Exiting,please wait...";
7 exit;
8 }
9 sleep 20s;
按下ctrl+c:
1 root@ubuntu:/home/dyx/linux 16:13:10 107# bash ex_trap.sh 2 ^CYou press Ctrl+C. 3 Exiting,please wait...
但是如果把trap函數發在前面就不可以了:
1 root@ubuntu:/home/dyx/linux 16:13:58 109# bash ex_trap.sh 2 You press Ctrl+C. 3 Exiting,please wait...
直接出現函數的內容了。
為什么????
一、trap捕捉到信號之后,可以有三種反應方式:
(1)執行一段程序來處理這一信號
(2)接受信號的默認操作
(3)忽視這一信號
二、trap對上面三種方式提供了三種基本形式:
第一種形式的trap命令在shell接收到signal list清單中數值相同的信號時,將執行雙
引號中的命令串。
trap 'commands' signal-list
trap "commands" signal-list
為了恢復信號的默認操作,使用第二種形式的trap命令:
trap signal-list
第三種形式的trap命令允許忽視信號
trap " " signal-list
三、注意:
(1) 對信號11(段違例)不能捕捉,因為shell本身需要捕捉該信號去進行內存的轉儲。
(2) 在trap中可以定義對信號0的處理(實際上沒有這個信號), shell程序在其終止(如
執行exit語句)時發出該信號。
(3) 在捕捉到signal-list中指定的信號并執行完相應的命令之后, 如果這些命令沒有
將shell程序終止的話,shell程序將繼續執行收到信號時所執行的命令后面的命令,這樣將
很容易導致shell程序無法終止。
另外,在trap語句中,單引號和雙引號是不同的,當shell程序第一次碰到trap語句時,將
把commands中的命令掃描一遍。此時若commands是用單引號括起來的話,那么shell不會
對commands中的變量和命令進行替換, 否則commands中的變量和命令將用當時具體的值來替
換。
四、例子:忽略Ctrl+C:
在有些情況下,我們不希望自己的shell腳本在運行時刻被中斷,比如說我們寫得shell腳
本設為某一用戶的默認shell,使這一用戶進入系統后只能作某一項工作,如數據庫備份,
我們可不希望用戶使用ctrl+C之類便進入到shell狀態,做我們不希望做的事情。這便用到
了信號處理。
kill -l可以列出系統的信號名稱,如下:
-bash-3.00# kill -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 17) SIGCHLD
18) SIGCONT 19) SIGSTOP 20) SIGTSTP 21) SIGTTIN
22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO
30) SIGPWR 31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1
36) SIGRTMIN+2 37) SIGRTMIN+3 38) SIGRTMIN+4 39) SIGRTMIN+5
40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8 43) SIGRTMIN+9
44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13
52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9
56) SIGRTMAX-8 57) SIGRTMAX-7 58) SIGRTMAX-6 59) SIGRTMAX-5
60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2 63) SIGRTMAX-1
64) SIGRTMAX
通常我們需要忽略的信號有四個,即:HUP, INT, QUIT, TSTP,也就是信號1, 2, 3, 24
使用這樣的語句可以使這些中斷信號被忽略:
trap "" 1 2 3 24 或 trap "" HUP INT QUIT TSTP
用 trap :1 2 3 24 或 trap HUP INT QUIT TSTP使其回復默認值。
用stty -a可以列出中斷信號與鍵盤的對應,分別執行上面的命令后,運行
tail -f /etc/passwd, 然后嘗試用鍵盤中斷,試試兩種情況(默認和忽略)下有何不同。
更方便的是我們可以用在shell中用trap定義我們自己的信號處理程序,就象在c中用
signal一樣,
如:trap “echo 'GO Away'” INT
Unix信號處理學習筆記
(轉自:http://www.cppblog.com/powervv/archive/2008/06/30/54934.html )
信號是傳送給進程的一種事件通知,生成信號的事件有三大類:
1.程序錯誤:除零,非法內存訪問…
2.外部信號:終端Ctrl-C產生SGINT信號,定時器到期產生SIGALRM…
3.顯示請求:kill函數允許進程發送任何信號給其他進程或進程組。
信號生成既可以是同步的(信號與程序中的某個具體操作相關并在那個操作同時產生),也可以是異步的。通常程序錯誤生成信號為同步的,進程顯式請求給自己的信號也是同步的。
外部事件總是異步的,來自其他進程的顯示請求也是異步的。
信號發生時,我們可以告訴unix內核采取下面三種動作中的任一種:
1.忽略信號:大部分信號可被忽略,除SIGSTOP和SIGKILL信號外(這是超級用戶殺掉或停掉任意進程的手段)。
2.捕獲信號:注冊信號處理函數,它對產生的特定信號做處理。
3.讓信號默認動作起作用:unix內核定義的默認動作,有5種情況:
a)流產abort:終止進程并產生core文件。
b)終止stop:終止進程但不生成core文件。
c)忽略:忽略信號。
d)掛起suspend:掛起進程。
e)繼續continue:若進程是掛起的,則resume進程,否則忽略此信號。
任意時刻,進程可以為信號指定動作。
信號處理涉及兩個過程,生成與交付。
信號生成出現在事件發生時,此時內核檢查接收進程的相關數據結構,此結構中記錄了信號的布局,懸掛信號集和處理動作。如果信號是要被忽 略的,內核不做任何動作就返回。否則,將此信號加入懸掛信號集合中。(懸掛信號集合通常用位串表示,每位對應一個信號,內核無法記錄同一信號的多個實 例)。
如果進程處于可中斷的睡眠狀態,并且該信號非阻塞,內核喚醒進 程。被喚醒進程一旦運行則在返回用戶態前優先處理懸掛信號,當有懸掛信號并且非阻塞時,內核查看是否有處理句柄,如果沒有注冊句柄,則采取默認動作(通常 為終止進程)。如果有句柄,則將此信號加入阻塞信號屏蔽中。
最后內核安排進程返回到用戶態并執行信號句柄,同時保證句柄執行完時,進程從被中斷處代碼執行。
由異步事件產生的信號可能在任一條指令后發生,當信號句柄完成時,進程從中斷之處起執行。如果信號是在進程處于系統調用期間到達的,內核通常abort此系統調用并返回錯誤碼EINTR。
進程可以有選擇的阻塞信號交付,當一個被阻塞的信號生成時,如果進程指定的動作為默認或者捕獲,則此信號一直懸掛于該進程直到對此信號的阻塞放開,或者信號動作改為忽略。系統對阻塞信號的判定是在信號交付時而非生成時,這樣可以允許進程在信號被交付前改變信號動作。
每個進程有一個阻塞信號屏蔽,它定義當前被阻塞交付的那些信號。可認為它是一個位串,每位對應一個信號。如果某信號對應的位被設置,則該信號當前阻塞,進程可調用
sigprocmask函數來檢查或設置屏蔽。
程序錯誤類信號:默認動作使進程流產,產生core文件。
SIGABRT:調用abort函數生成的信號。
SIGFPE:浮點計算錯誤。
SIGILL:非法指令錯誤。
SIGBUS/SIGSEGV:硬件錯誤-非法地址訪問。
SIGEMT:硬件錯誤
SIGSYS:非法系統調用。
SIGTRAP:硬件錯誤(通常為斷點指令)。
程序終止類信號:默認動作使進程終止,我們通常要處理這類信號,做一些清理工作,句柄函數應在結束時為此信號指定默認動作,然后再次生成該信號,使得程序終止。
SIGHUP:終端斷開連接時,生成此信號給控制進程。
SIGINT:Ctrl-C或Delete按下時,由終端驅動生成,并發送給前臺進程組中的所有進程。
SIGKILL:使程序立即終止,不能被捕獲或忽略,也不能被阻塞。
SIGQUIT:Ctrl-\,如SIGINT,并且產生core。
SIGTERM:該信號使程序終止,但是可以阻塞、捕獲、忽略。
鬧鐘類信號:通知定時器到期,默認動作是終止程序,但通常會設置句柄。
SIGALRM:alarm/setitimer函數設置定時到期后,會產生此信號。
SIGPROF:
SIGVTALRM:
I/O類信號:通知進程在描述字上發生了感興趣事件,支持信號驅動IO。
SIGIO:fd準備執行輸入輸出時發送此信號。
SIGPOLL:異步I/O信號。
SIGURG:網絡收到帶外數據時可選擇生成此信號。
作業控制類信號:
SIGCHLD:進程終止或停止時會向其父進程發送該信號,默認動作為忽略。
SIGCONT:使停止的進程恢復運行。
SIGSTOP:停止進程。
SIGTSTP/SIGTTIN/SIGTTOU:
操作錯誤類信號:默認動作終止程序。
SIGPIPE:管道破裂。
SIGXCPU/SIGXFSZ:
signal函數:
void (* signal(int sig, void (*func)(int)))(int);
sig指明是哪一種信號。
func指明動作:SIG_DFL,SIG_IGN,或者信號句柄地址。
當信號發生時,如果func指向信號句柄,系統在將控制轉往句柄前,先將該信號動作置為DFL,或者阻塞該信號直到句柄完成。
Signal函數返回值指向前一次有效動作指針:SIG_DFL,SIG_IGN,或信號地址,這提供了恢復信號動作的機制。如果signal調用出錯,返回SIG_ERR并設置errno。
進程初啟時的信號動作:
fork:繼承父進程的動作
exec:所有信號動作要么是忽略要么是默認。
不可靠信號:
早期版本Unix中使用signal,每當信號交付時,其動作總是由系統重置為默認動作,因此為了使信號句柄執行期間,仍能對同一信號后續做反應,需要再次調用signal。
Catch_Signal(){
//如果第二次信號剛好在此時發生,將導致進程終止core掉。
signal(SIGQUIT,Catch_Signal);
}
Main(){
signal(SIGQUIT, Catch_Signal);
…
}
使用signal的另一個問題是,對于信號,進程要么忽略,要么捕獲,無法在一段時間內阻塞信號(推遲信號的交付),為了克服signal兼容性問題,現代unix均實現了POSIX定義的sigaction函數,該函數采用一個sigaction結構,除定義信號交付時要采取的動作外,還包含其他一些動作控制信息。
sigaction還允許調用進程檢測或指定與特定信號相關的動作。
int sigaction(int sig, const struct sigaction* act, struct sigaction* oact);
sig指定信號-除SIGKILL和SIGSTOP。
如果act為NULL,則不改變信號動作,只查詢當前動作。
成功返回0,失敗則不安裝新信號動作,返回-1,設置errno。
struct sigaction{
void(*sa_handler)(int);//同signal的第二參數func。
void(*sa_sigaction)(int, siginfo_t*, void*); //僅當flags設置SA_SIGINFO起作用。
sigset_t sa_mask;//指明信號執行期間要阻塞的一組信號,除此之外導致信號句柄執
行的信號也自動阻塞,除非指定了SA_NODEFER。信號句柄正常返回時,屏蔽恢復到原先狀態。
int sa_flags;//
};
sa_flags是一個位串,可以通過或運算生成。可設置下列標志:
SA_NOCLDSTOP:只對CHLD信號起作用,子進程暫停時不發信號給父進程。
SA_RESTART:信號句柄返回時,自動恢復被該信號中斷的系統調用。否則該系統調用將中斷返回-1,并設置errno為EINTR。
SA_ONSTACK:
SA_RESETHAND:信號句柄入口,系統將重置信號動作為SIG_DFL.
SA_NODEFER:句柄執行期間,不自動阻塞該信號。
SA_NOCLDWAIT:只對CHLD起作用,調用進程的所有子進程在終止時不會成為Zombe。這種情況下,父進程無需要wait子進程,并且子進程終止也不向父進程發SIGCHLD信號。
如果父進程調用wait,將阻塞到所有子進程終止,并返回-1,errno設為ECHILD.
SA_SIGINFO:如果未設置此標志,則信號句柄原型為:
void func(int signo);
如果設置此標志,則句柄原型為:
void func(int signo, singinfo_t* info, void* context);
Info-解釋信號生成的原因。
Context-信號被交付時所中斷進程的上下文。
一旦用sigaction為特定信號建立了動作,該動作就一直保持,直到另一次調用sigaction,或者調用exec,或者因設置了SA_RESETHAND導致系統自動改變動作為默認為止。
除了外部中斷產生信號外,程序可以顯式的調用raise函數給他自己發送信號,或調用kill向自己或其他進程發送信號。
阻塞信號意味著保持該信號并推遲它的交付,可以防止程序中的關鍵代碼被信號中斷。
信號集操作:
int sigemptyset(sigset_t* set); //清空信號集
int sigfillset(sigset_t* set);//包含所有信號集
sigaddset/sigdelset/sigismember;
sigprocmask用來檢測或改變調用進程的信號屏蔽。
int sigprocmask(int how, const sigset_t* set, sigset_t* oset);
How: SIGBLOCKSIG_UNBLOCKSIG_SETMASK
如果調用sigprocmask放開某個信號而導致任何懸掛信號被解除阻塞,則函數返回前,這些信號中至少有一個被交付。
檢查懸掛信號:int sigpending(sigset_t* set);
等待信號:int pause(void);
懸掛調用進程直到有一個信號到達。僅當句柄執行并返回時,pause函數才返回:此時返回-1,并設置errno為EINTR。所有其他情況下pause不返回。
如果多個相同信號在信號句柄運行前發給了進程,則句柄只被運行一次。換句話說,默認情況下unix信號是非排隊的,只有當實現支持實時信號并且sa_flags設置SA_SIGINFO時,由sigqueue生成的后續信號才排隊。
I/O執行期間,有可能到達信號,此時有兩種情況:重新開始系統調用還是返回失敗.
早期unix特征為,進程執行慢系統調用期間捕獲信號時,該調用被中斷并設置errno為EINTR。現代unix增加了sa_flags選項SA_RESTART可對單個信號要求自動恢復被中斷的系統調用。
原則如下:如果進程阻塞于慢系統調用,并且進程捕獲信號且該信號句柄返回,系統調用可能返回EINTR。雖然有些unix能自動恢復系統調用,但是為了兼容性,我們必須準備慢系統調用返回EINTR,當檢測到EINTR,要么重新開始系統調用,要么做其他處理。
Again:
if (n=read(fd,buff, BUFSIZE) < 0) {
if (errno ==EINTR)
goto Again;
else
…}
參考:http://blog.chinaunix.net/uid-20648182-id-1907477.html
名稱 默認動作 說明
SIGHUP 終止進程 終端線路掛斷
SIGINT終止進程 中斷進程
SIGQUIT建立CORE文件終止進程,并且生成core文件
SIGILL建立CORE文件 非法指令
SIGTRAP 建立CORE文件 跟蹤自陷
SIGBUS建立CORE文件 總線錯誤
SIGSEGV建立CORE文件 段非法錯誤
SIGFPE 建立CORE文件 浮點異常
SIGIOT 建立CORE文件 執行I/O自陷
SIGKILL 終止進程 殺死進程
SIGPIPE 終止進程 向一個沒有讀進程的管道寫數據
SIGALARM 終止進程 計時器到時
SIGTERM 終止進程 軟件終止信號
SIGSTOP 停止進程 非終端來的停止信號
SIGTSTP 停止進程 終端來的停止信號
SIGCONT 忽略信號 繼續執行一個停止的進程
SIGURG 忽略信號 I/O緊急信號
SIGIO忽略信號 描述符上可以進行I/O
SIGCHLD忽略信號 當子進程停止或退出時通知父進程
SIGTTOU 停止進程 后臺進程寫終端
SIGTTIN 停止進程 后臺進程讀終端
SIGXGPU 終止進程 CPU時限超時
SIGXFSZ 終止進程 文件長度過長
SIGWINCH 忽略信號 窗口大小發生變化
SIGPROF 終止進程 統計分布圖用計時器到時
SIGUSR1 終止進程 用戶定義信號1
SIGUSR2 終止進程 用戶定義信號2
SIGVTALRM 終止進程 虛擬計時器到時
總結
以上是生活随笔為你收集整理的linux中脚本扑捉(trap)信号问题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: HDU 6631 line symmet
- 下一篇: [小工具] 使用 uTools 进行内网