linux 信號列表及分析

我們運行如下命令,可看到Linux支持的信號列表:

~$ 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

列表中,編號爲1 ~ 31的信號爲傳統UNIX支持的信號,是不可靠信號(非實時的),編號爲32 ~ 63的信號是後來擴充的,稱做可靠信號(實時信號)。不可靠信號和可靠信號的區別在於前者不支持排隊,可能會造成信號丟失,而後者不會。

下面我們對編號小於SIGRTMIN的信號進行討論。

1) SIGHUP
本信號在用戶終端連接(正常或非正常)結束時發出, 通常是在終端的控制進程結束時, 通知同一session內的各個作業, 這時它們與控制終端不再關聯。

登錄Linux時,系統會分配給登錄用戶一個終端(Session)。在這個終端運行的所有程序,包括前臺進程組和 後臺進程組,一般都屬於這個 Session。當用戶退出Linux登錄時,前臺進程組和後臺有對終端輸出的進程將會收到SIGHUP信號。這個信號的默認操作爲終止進程,因此前臺進 程組和後臺有終端輸出的進程就會中止。不過可以捕獲這個信號,比如wget能捕獲SIGHUP信號,並忽略它,這樣就算退出了Linux登錄,wget也 能繼續下載。

此外,對於與終端脫離關係的守護進程,這個信號用於通知它重新讀取配置文件。

2) SIGINT
程序終止(interrupt)信號, 在用戶鍵入INTR字符(通常是Ctrl-C)時發出,用於通知前臺進程組終止進程。

3) SIGQUIT
和SIGINT類似, 但由QUIT字符(通常是Ctrl-\)來控制. 進程在因收到SIGQUIT退出時會產生core文件, 在這個意義上類似於一個程序錯誤信號。

4) SIGILL
執行了非法指令. 通常是因爲可執行文件本身出現錯誤, 或者試圖執行數據段. 堆棧溢出時也有可能產生這個信號。

5) SIGTRAP
由斷點指令或其它trap指令產生. 由debugger使用。

6) SIGABRT
調用abort函數生成的信號。

7) SIGBUS
非法地址, 包括內存地址對齊(alignment)出錯。比如訪問一個四個字長的整數, 但其地址不是4的倍數。它與SIGSEGV的區別在於後者是由於對合法存儲地址的非法訪問觸發的(如訪問不屬於自己存儲空間或只讀存儲空間)。

8) SIGFPE
在發生致命的算術運算錯誤時發出. 不僅包括浮點運算錯誤, 還包括溢出及除數爲0等其它所有的算術的錯誤。

9) SIGKILL
用來立即結束程序的運行. 本信號不能被阻塞、處理和忽略。如果管理員發現某個進程終止不了,可嘗試發送這個信號。

10) SIGUSR1
留給用戶使用

11) SIGSEGV
試圖訪問未分配給自己的內存, 或試圖往沒有寫權限的內存地址寫數據.

12) SIGUSR2
留給用戶使用

13) SIGPIPE
管道破裂。這個信號通常在進程間通信產生,比如採用FIFO(管道)通信的兩個進程,讀管道沒打開或者意外終止就往管道寫,寫進程會收到SIGPIPE信號。此外用Socket通信的兩個進程,寫進程在寫Socket的時候,讀進程已經終止。

14) SIGALRM
時鐘定時信號, 計算的是實際的時間或時鐘時間. alarm函數使用該信號.

15) SIGTERM
程序結束(terminate)信號, 與SIGKILL不同的是該信號可以被阻塞和處理。通常用來要求程序自己正常退出,shell命令kill缺省產生這個信號。如果進程終止不了,我們纔會嘗試SIGKILL。

17) SIGCHLD
子進程結束時, 父進程會收到這個信號。

如果父進程沒有處理這個信號,也沒有等待(wait)子進程,子進程雖然終止,但是還會在內核進程表中佔有表項,這 時的子進程稱爲殭屍進程。這種情 況我們應該避免(父進程或者忽略SIGCHILD信號,或者捕捉它,或者wait它派生的子進程,或者父進程先終止,這時子進程的終止自動由init進程 來接管)。

18) SIGCONT
讓一個停止(stopped)的進程繼續執行. 本信號不能被阻塞. 可以用一個handler來讓程序在由stopped狀態變爲繼續執行時完成特定的工作. 例如, 重新顯示提示符

19) SIGSTOP
停止(stopped)進程的執行. 注意它和terminate以及interrupt的區別:該進程還未結束, 只是暫停執行. 本信號不能被阻塞, 處理或忽略.

20) SIGTSTP
停止進程的運行, 但該信號可以被處理和忽略. 用戶鍵入SUSP字符時(通常是Ctrl-Z)發出這個信號

21) SIGTTIN
當後臺作業要從用戶終端讀數據時, 該作業中的所有進程會收到SIGTTIN信號. 缺省時這些進程會停止執行.

22) SIGTTOU
類似於SIGTTIN, 但在寫終端(或修改終端模式)時收到.

23) SIGURG
有”緊急”數據或out-of-band數據到達socket時產生.

24) SIGXCPU
超過CPU時間資源限制. 這個限制可以由getrlimit/setrlimit來讀取/改變。

25) SIGXFSZ
當進程企圖擴大文件以至於超過文件大小資源限制。

26) SIGVTALRM
虛擬時鐘信號. 類似於SIGALRM, 但是計算的是該進程佔用的CPU時間.

27) SIGPROF
類似於SIGALRM/SIGVTALRM, 但包括該進程用的CPU時間以及系統調用的時間.

28) SIGWINCH
窗口大小改變時發出.

29) SIGIO
文件描述符準備就緒, 可以開始進行輸入/輸出操作.

30) SIGPWR
Power failure

31) SIGSYS
非法的系統調用。

在以上列出的信號中,程序不可捕獲、阻塞或忽略的信號有:SIGKILL,SIGSTOP
不能恢復至默認動作的信號有:SIGILL,SIGTRAP
默認會導致進程流產的信號有:SIGABRT,SIGBUS,SIGFPE,SIGILL,SIGIOT,SIGQUIT,SIGSEGV,SIGTRAP,SIGXCPU,SIGXFSZ
默認會導致進程退出的信號有:SIGALRM,SIGHUP,SIGINT,SIGKILL,SIGPIPE,SIGPOLL,SIGPROF,SIGSYS,SIGTERM,SIGUSR1,SIGUSR2,SIGVTALRM
默認會導致進程停止的信號有:SIGSTOP,SIGTSTP,SIGTTIN,SIGTTOU
默認進程忽略的信號有:SIGCHLD,SIGPWR,SIGURG,SIGWINCH

此外,SIGIO在SVR4是退出,在4.3BSD中是忽略;SIGCONT在進程掛起時是繼續,否則是忽略,不能被阻塞


信號是Linux編程中非常重要的部分,本文將詳細介紹信號機制的基本概念、Linux對信號機制的大致實現方法、如何使用信號,以及有關信號的幾個系統調用。

  信號機制是進程之間相互傳遞消息的一種方法,信號全稱爲軟中斷信號,也有人稱作軟中斷。從它的命名可以看出,它的實質和使用很象中斷。所以,信號可以說是進程控制的一部分。

1.信號的基本概念

  本節先介紹信號的一些基本概念,然後給出一些基本的信號類型和信號對應的事件。基本概念對於理解和使用信號,對於理解信號機制都特別重要。下面就來看看什麼是信號。

1.1 基本概念

  軟中斷信號(signal,又簡稱爲信號)用來通知進程發生了異步事件。進程之間可以互相通過系統調用kill發送軟中斷信號。內核也可以因爲 內部事件而給進程發送信號,通知進程發生了某個事件。注意,信號只是用來通知某進程發生了什麼事件,並不給該進程傳遞任何數據。

  收到信號的進程對各種信號有不同的處理方法。處理方法可以分爲三類:第一種是類似中斷的處理程序,對於需要處理的信號,進程可以指定處理函數, 由該函數來處理。第二種方法是,忽略某個信號,對該信號不做任何處理,就象未發生過一樣。第三種方法是,對該信號的處理保留系統的默認值,這種缺省操作, 對大部分的信號的缺省操作是使得進程終止。進程通過系統調用signal來指定進程對某個信號的處理行爲。

  在進程表的表項中有一個軟中斷信號域,該域中每一位對應一個信號,當有信號發送給進程時,對應位置位。由此可以看出,進程對不同的信號可以同時保留,但對於同一個信號,進程並不知道在處理之前來過多少個。

1.2 信號的類型

  發出信號的原因很多,這裏按發出信號的原因簡單分類,以瞭解各種信號:

  1. 與進程終止相關的信號。當進程退出,或者子進程終止時,發出這類信號。
  2. 與進程例外事件相關的信號。如進程越界,或企圖寫一個只讀的內存區域(如程序正文區),或執行一個特權指令及其他各種硬件錯誤。
  3. 與在系統調用期間遇到不可恢復條件相關的信號。如執行系統調用exec時,原有資源已經釋放,而目前系統資源又已經耗盡。
  4. 與執行系統調用時遇到非預測錯誤條件相關的信號。如執行一個並不存在的系統調用。
  5. 在用戶態下的進程發出的信號。如進程調用系統調用kill向其他進程發送信號。
  6. 與終端交互相關的信號。如用戶關閉一個終端,或按下break鍵等情況。
  7. 跟蹤進程執行的信號。

  Linux支持的信號列表如下。很多信號是與機器的體系結構相關的。

  首先列出的是POSIX.1中列出的信號:

信號   值  處理動作 發出信號的原因
----------------------------------------------------------------------
SIGHUP 1     A  終端掛起或者控制進程終止
SIGINT 2     A  鍵盤中斷(如break鍵被按下)
SIGQUIT 3     C  鍵盤的退出鍵被按下
SIGILL 4     C  非法指令
SIGABRT 6     C  由abort(3)發出的退出指令
SIGFPE 8     C  浮點異常
SIGKILL 9     AEF  Kill信號
SIGSEGV 11     C  無效的內存引用
SIGPIPE 13     A  管道破裂: 寫一個沒有讀端口的管道
SIGALRM 14    A  由alarm(2)發出的信號
SIGTERM 15    A  終止信號
SIGUSR1 30,10,16 A  用戶自定義信號1
SIGUSR2 31,12,17 A  用戶自定義信號2
SIGCHLD 20,17,18 B  子進程結束信號
SIGCONT 19,18,25    進程繼續(曾被停止的進程)
SIGSTOP 17,19,23 DEF 終止進程
SIGTSTP 18,20,24 D  控制終端(tty)上按下停止鍵
SIGTTIN 21,21,26 D  後臺進程企圖從控制終端讀
SIGTTOU 22,22,27 D  後臺進程企圖從控制終端寫

  下面的信號沒在POSIX.1中列出,而在SUSv2列出

信號    值   處理動作 發出信號的原因
--------------------------------------------------------------------
SIGBUS 10,7,10 C 總線錯誤(錯誤的內存訪問)
SIGPOLL A Sys V定義的Pollable事件,與SIGIO同義
SIGPROF 27,27,29 A Profiling定時器到
SIGSYS 12,-,12 C 無效的系統調用 (SVID)
SIGTRAP 5 C 跟蹤/斷點捕獲
SIGURG 16,23,21 B Socket出現緊急條件(4.2 BSD)
SIGVTALRM 26,26,28 A 實際時間報警時鐘信號(4.2 BSD)
SIGXCPU 24,24,30 C 超出設定的CPU時間限制(4.2 BSD)
SIGXFSZ 25,25,31 C 超出設定的文件大小限制(4.2 BSD)

(對於SIGSYS,SIGXCPU,SIGXFSZ,以及某些機器體系結構下的SIGBUS,Linux缺省的動作是A (terminate),SUSv2 是C (terminate and dump core))。

  下面是其它的一些信號:

信號    值   處理動作 發出信號的原因
----------------------------------------------------------------------
SIGIOT 6 C IO捕獲指令,與SIGABRT同義
SIGEMT 7,-,7 SIGSTKFLT -,16,- A 協處理器堆棧錯誤
SIGIO 23,29,22 A 某I/O操作現在可以進行了(4.2 BSD)
SIGCLD -,-,18 A 與SIGCHLD同義
SIGPWR 29,30,19 A 電源故障(System V)
SIGINFO 29,-,- A 與SIGPWR同義
SIGLOST -,-,- A 文件鎖丟失
SIGWINCH 28,28,20 B 窗口大小改變(4.3 BSD, Sun)
SIGUNUSED -,31,- A 未使用的信號(will be SIGSYS)

(在這裏,- 表示信號沒有實現;有三個值給出的含義爲,第一個值通常在Alpha和Sparc上有效,中間的值對應i386和ppc以及sh,最後一個值對應mips。信號29在Alpha上爲SIGINFO / SIGPWR ,在Sparc上爲SIGLOST。)

  處理動作一項中的字母含義如下:

  • A 缺省的動作是終止進程
  • B 缺省的動作是忽略此信號
  • C 缺省的動作是終止進程並進行內核映像轉儲(dump core)
  • D 缺省的動作是停止進程
  • E 信號不能被捕獲
  • F 信號不能被忽略

  上面介紹的信號是常見系統所支持的。以表格的形式介紹了各種信號的名稱、作用及其在默認情況下的處理動作。各種默認處理動作的含義是:終止程序 是指進程退出;忽略該信號是將該信號丟棄,不做處理;停止程序是指程序掛起,進入停止狀況以後還能重新進行下去,一般是在調試的過程中(例如ptrace 系統調用);內核映像轉儲是指將進程數據在內存的映像和進程在內核結構中存儲的部分內容以一定格式轉儲到文件系統,並且進程退出執行,這樣做的好處是爲程 序員提供了方便,使得他們可以得到進程當時執行時的數據值,允許他們確定轉儲的原因,並且可以調試他們的程序。

  注意: 信號SIGKILL和SIGSTOP既不能被捕捉,也不能被忽略。信號SIGIOT與SIGABRT是一個信號。可以看出,同一個信號在不同的系統中值可能不一樣,所以建議最好使用爲信號定義的名字,而不要直接使用信號的值。

2.信 號 機 制

  上一節中介紹了信號的基本概念,在這一節中,我們將介紹內核如何實現信號機制。即內核如何向一個進程發送信號、進程如何接收一個信號、進程怎樣 控制自己對信號的反應、內核在什麼時機處理和怎樣處理進程收到的信號。還要介紹一下setjmp和longjmp在信號中起到的作用。

2.1 內核對信號的基本處理方法

  內核給一個進程發送軟中斷信號的方法,是在進程所在的進程表項的信號域設置對應於該信號的位。這裏要補充的是,如果信號發送給一個正在睡眠的進 程,那麼要看該進程進入睡眠的優先級,如果進程睡眠在可被中斷的優先級上,則喚醒進程;否則僅設置進程表中信號域相應的位,而不喚醒進程。這一點比較重 要,因爲進程檢查是否收到信號的時機是:一個進程在即將從內核態返回到用戶態時;或者,在一個進程要進入或離開一個適當的低調度優先級睡眠狀態時。

  內核處理一個進程收到的信號的時機是在一個進程從內核態返回用戶態時。所以,當一個進程在內核態下運行時,軟中斷信號並不立即起作用,要等到將返回用戶態時才處理。進程只有處理完信號纔會返回用戶態,進程在用戶態下不會有未處理完的信號。

  內核處理一個進程收到的軟中斷信號是在該進程的上下文中,因此,進程必須處於運行狀態。前面介紹概念的時候講過,處理信號有三種類型:進程接收 到信號後退出;進程忽略該信號;進程收到信號後執行用戶設定用系統調用signal的函數。當進程接收到一個它忽略的信號時,進程丟棄該信號,就象沒有收 到該信號似的繼續運行。如果進程收到一個要捕捉的信號,那麼進程從內核態返回用戶態時執行用戶定義的函數。而且執行用戶定義的函數的方法很巧妙,內核是在 用戶棧上創建一個新的層,該層中將返回地址的值設置成用戶定義的處理函數的地址,這樣進程從內核返回彈出棧頂時就返回到用戶定義的函數處,從函數返回再彈 出棧頂時,才返回原先進入內核的地方。這樣做的原因是用戶定義的處理函數不能且不允許在內核態下執行(如果用戶定義的函數在內核態下運行的話,用戶就可以 獲得任何權限)。

  在信號的處理方法中有幾點特別要引起注意。

  1. 在一些系統中,當一個進程處理完中斷信號返回用戶態之前,內核清除用戶區中設定的對該信號的處理例程的地址,即下一次進程對 該信號的處理方法又改爲默認值,除非在下一次信號到來之前再次使用signal系統調用。這可能會使得進程在調用signal之前又得到該信號而導致退 出。在BSD中,內核不再清除該地址。但不清除該地址可能使得進程因爲過多過快的得到某個信號而導致堆棧溢出。爲了避免出現上述情況。在BSD系統中,內 核模擬了對硬件中斷的處理方法,即在處理某個中斷時,阻止接收新的該類中斷。
  2. 如果要捕捉的信號發生於進程正在一個系統調用 中時,並且該進程睡眠在可中斷的優先級上,這時該信號引起進程作一次longjmp,跳出睡眠狀態,返回用戶態並執行信號處理例程。當從信號處理例程返回 時,進程就象從系統調用返回一樣,但返回了一個錯誤代碼,指出該次系統調用曾經被中斷。這要注意的是,BSD系統中內核可以自動地重新開始系統調用。
  3. 若進程睡眠在可中斷的優先級上,則當它收到一個要忽略的信號時,該進程被喚醒,但不做longjmp,一般是繼續睡眠。但用戶感覺不到進程曾經被喚醒,而是象沒有發生過該信號一樣。
  4. 內 覈對子進程終止(SIGCLD)信號的處理方法與其他信號有所區別。當進程檢查出收到了一個子進程終止的信號時,缺省情況下,該進程就象沒有收到該信號似 的,如果父進程執行了系統調用wait,進程將從系統調用wait中醒來並返回wait調用,執行一系列wait調用的後續操作(找出僵死的子進程,釋放 子進程的進程表項),然後從wait中返回。SIGCLD信號的作用是喚醒一個睡眠在可被中斷優先級上的進程。如果該進程捕捉了這個信號,就象普通信號處 理一樣轉到處理例程。如果進程忽略該信號,那麼系統調用wait的動作就有所不同,因爲SIGCLD的作用僅僅是喚醒一個睡眠在可被中斷優先級上的進程, 那麼執行wait調用的父進程被喚醒繼續執行wait調用的後續操作,然後等待其他的子進程。

  如果一個進程調用signal系統調用,並設置了SIGCLD的處理方法,並且該進程有子進程處於僵死狀態,則內核將向該進程發一個SIGCLD信號。

2.2 setjmp和longjmp的作用

  前面在介紹信號處理機制時,多次提到了setjmp和longjmp,但沒有仔細說明它們的作用和實現方法。這裏就此作一個簡單的介紹。

  在介紹信號的時候,我們看到多個地方要求進程在檢查收到信號後,從原來的系統調用中直接返回,而不是等到該調用完成。這種進程突然改變其上下文 的情況,就是使用setjmp和longjmp的結果。setjmp將保存的上下文存入用戶區,並繼續在舊的上下文中執行。這就是說,進程執行一個系統調 用,當因爲資源或其他原因要去睡眠時,內核爲進程作了一次setjmp,如果在睡眠中被信號喚醒,進程不能再進入睡眠時,內核爲進程調用longjmp, 該操作是內核爲進程將原先setjmp調用保存在進程用戶區的上下文恢復成現在的上下文,這樣就使得進程可以恢復等待資源前的狀態,而且內核爲 setjmp返回1,使得進程知道該次系統調用失敗。這就是它們的作用。

3.有關信號的系統調用

  前面兩節已經介紹了有關信號的大部分知識。這一節我們來了解一下這些系統調用。其中,系統調用signal是進程用來設定某個信號的處理方法, 系統調用kill是用來發送信號給指定進程的。這兩個調用可以形成信號的基本操作。後兩個調用pause和alarm是通過信號實現的進程暫停和定時器, 調用alarm是通過信號通知進程定時器到時。所以在這裏,我們還要介紹這兩個調用。

3.1 signal 系統調用

  系統調用signal用來設定某個信號的處理方法。該調用聲明的格式如下:

void (*signal(int signum, void (*handler)(int)))(int);

  在使用該調用的進程中加入以下頭文件:

#include 

  上述聲明格式比較複雜,如果不清楚如何使用,也可以通過下面這種類型定義的格式來使用(POSIX的定義):

typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);

  但這種格式在不同的系統中有不同的類型定義,所以要使用這種格式,最好還是參考一下聯機手冊。

  在調用中,參數signum指出要設置處理方法的信號。第二個參數handler是一個處理函數,或者是

  • SIG_IGN:忽略參數signum所指的信號。
  • SIG_DFL:恢復參數signum所指信號的處理方法爲默認值。

  傳遞給信號處理例程的整數參數是信號值,這樣可以使得一個信號處理例程處理多個信號。系統調用signal返回值是指定信號signum前一次的處理例程或者錯誤時返回錯誤代碼SIG_ERR。下面來看一個簡單的例子:

#include 
#include 
#include 
void sigroutine(int dunno)
{ /* 信號處理例程,其中dunno將會得到信號的值 */
switch (dunno) {
case 1:
printf("Get a signal -- SIGHUP ");
break;
case 2:
printf("Get a signal -- SIGINT ");
break;
case 3:
printf("Get a signal -- SIGQUIT ");
break;
}
return;
}

int main() {

printf("process id is %d ",getpid());
signal(SIGHUP, sigroutine); //* 下面設置三個信號的處理方法
signal(SIGINT, sigroutine);
signal(SIGQUIT, sigroutine);

for (;;) ;
}

  其中信號SIGINT由按下Ctrl-C發出,信號SIGQUIT由按下Ctrl-(back slash)發出。該程序執行的結果如下:

localhost:~$ ./sig_test
process id is 463
Get a signal -SIGINT //按下Ctrl-C得到的結果
Get a signal -SIGQUIT //按下Ctrl-得到的結果
//按下Ctrl-z將進程置於後臺
[1]+ Stopped ./sig_test
localhost:~$ bg
[1]+ ./sig_test &
localhost:~$ kill -HUP 463 //向進程發送SIGHUP信號
localhost:~$ Get a signal – SIGHUP
kill -9 463 //向進程發送SIGKILL信號,終止進程
localhost:~$

3.2 kill 系統調用

  系統調用kill用來向進程發送一個信號。該調用聲明的格式如下:

int kill(pid_t pid, int sig);

  在使用該調用的進程中加入以下頭文件:

#include 
#include 

  該系統調用可以用來向任何進程或進程組發送任何信號。如果參數pid是正數,那麼該調用將信號sig發送到進程號爲pid的進程。如果pid等 於0,那麼信號sig將發送給當前進程所屬進程組裏的所有進程。如果參數pid等於-1,信號sig將發送給除了進程1和自身以外的所有進程。如果參數 pid小於-1,信號sig將發送給屬於進程組-pid的所有進程。如果參數sig爲0,將不發送信號。該調用執行成功時,返回值爲0;錯誤時,返回 -1,並設置相應的錯誤代碼errno。下面是一些可能返回的錯誤代碼:

  • EINVAL:指定的信號sig無效。
  • ESRCH:參數pid指定的進程或進程組不存在。注意,在進程表項中存在的進程,可能是一個還沒有被wait收回,但已經終止執行的僵死進程。
  • EPERM: 進程沒有權力將這個信號發送到指定接收信號的進程。因爲,一個進程被允許將信號發送到進程pid時,必須擁有root權力,或者是發出調用的進程的UID 或EUID與指定接收的進程的UID或保存用戶ID(savedset-user-ID)相同。如果參數pid小於-1,即該信號發送給一個組,則該錯誤 表示組中有成員進程不能接收該信號。

3.3 pause系統調用

  系統調用pause的作用是等待一個信號。該調用的聲明格式如下:

int pause(void);

  在使用該調用的進程中加入以下頭文件:

#include 

  該調用使得發出調用的進程進入睡眠,直到接收到一個信號爲止。該調用總是返回-1,並設置錯誤代碼爲EINTR(接收到一個信號)。下面是一個簡單的範例:

#include 
#include 
#include 
void sigroutine(int unused)
{
printf("Catch a signal SIGINT ");
}

int main() {
signal(SIGINT, sigroutine);
pause();
printf("receive a signal ");
}

  在這個例子中,程序開始執行,就象進入了死循環一樣,這是因爲進程正在等待信號,當我們按下Ctrl-C時,信號被捕捉,並且使得pause退出等待狀態。

3.4 alarm和 setitimer系統調用

  系統調用alarm的功能是設置一個定時器,當定時器計時到達時,將發出一個信號給進程。該調用的聲明格式如下:

unsigned int alarm(unsigned int seconds);

  在使用該調用的進程中加入以下頭文件:

#include 

  系統調用alarm安排內核爲調用進程在指定的seconds秒後發出一個SIGALRM的信號。如果指定的參數seconds爲0,則不再發 送SIGALRM信號。後一次設定將取消前一次的設定。該調用返回值爲上次定時調用到發送之間剩餘的時間,或者因爲沒有前一次定時調用而返回0。注意,在 使用時,alarm只設定爲發送一次信號,如果要多次發送,就要多次使用alarm調用。

  對於alarm,這裏不再舉例。現在的系統中很多程序不再使用alarm調用,而是使用setitimer調用來設置定時器,用getitimer來得到定時器的狀態,這兩個調用的聲明格式如下:

int getitimer(int which, struct itimerval *value);
int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue);

  在使用這兩個調用的進程中加入以下頭文件:

#include 

  該系統調用給進程提供了三個定時器,它們各自有其獨有的計時域,當其中任何一個到達,就發送一個相應的信號給進程,並使得計時器重新開始。三個計時器由參數which指定,如下所示:

  • TIMER_REAL:按實際時間計時,計時到達將給進程發送SIGALRM信號。
  • ITIMER_VIRTUAL:僅當進程執行時才進行計時。計時到達將發送SIGVTALRM信號給進程。
  • ITIMER_PROF:當進程執行時和系統爲該進程執行動作時都計時。與ITIMER_VIR-TUAL是一對,該定時器經常用來統計進程在用戶態和內核態花費的時間。計時到達將發送SIGPROF信號給進程。

  定時器中的參數value用來指明定時器的時間,其結構如下:

struct itimerval {
struct timeval it_interval; /* 下一次的取值 */
struct timeval it_value; /* 本次的設定值 */
};

  該結構中timeval結構定義如下:

struct timeval {
long tv_sec; /* 秒 */
long tv_usec; /* 微秒,1秒 = 1000000 微秒*/
};

  在setitimer調用中,參數ovalue如果不爲空,則其中保留的是上次調用設定的值。定時器將it_value遞減到0時,產生一個信 號,並將it_value的值設定爲it_interval的值,然後重新開始計時,如此往復。當it_value設定爲0時,計時器停止,或者當它計時 到期,而it_interval爲0時停止。調用成功時,返回0;錯誤時,返回-1,並設置相應的錯誤代碼errno:

  • EFAULT:參數value或ovalue是無效的指針。
  • EINVAL:參數which不是ITIMER_REAL、ITIMER_VIRT或ITIMER_PROF中的一個。

  下面是關於setitimer調用的一個簡單示範,在該例子中,每隔一秒發出一個SIGALRM,每隔0.5秒發出一個SIGVTALRM信號:

#include 
#include 
#include 
#include 

int sec;
void sigroutine(int signo) {
switch (signo) {
case SIGALRM:
printf("Catch a signal -- SIGALRM ");
break;
case SIGVTALRM:
printf("Catch a signal -- SIGVTALRM ");
break;
}
return;
}

int main() {
struct itimerval value,ovalue,value2;
sec = 5;
printf("process id is %d ",getpid());
signal(SIGALRM, sigroutine);
signal(SIGVTALRM, sigroutine);
value.it_value.tv_sec = 1;
value.it_value.tv_usec = 0;
value.it_interval.tv_sec = 1;
value.it_interval.tv_usec = 0;
setitimer(ITIMER_REAL, &value, &ovalue);

value2.it_value.tv_sec = 0;
value2.it_value.tv_usec = 500000;
value2.it_interval.tv_sec = 0;
value2.it_interval.tv_usec = 500000;
setitimer(ITIMER_VIRTUAL, &value2, &ovalue);
for (;;) ;
}

  該例子的屏幕拷貝如下:

localhost:~$ ./timer_test
process id is 579
Catch a signal – SIGVTALRM
Catch a signal – SIGALRM
Catch a signal – SIGVTALRM
Catch a signal – SIGVTALRM
Catch a signal – SIGALRM
Catch a signal –GVTALRM

  本文簡單介紹了Linux下的信號,如果希望瞭解其他調用,請參考聯機手冊或其他文檔。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章