天天看點

程序間通信之-信号signal--linux核心剖析(九) 信号及信号來源 linux信号的發展及種類 信号的發送 信号的安裝(設定信号關關聯作) 信号進階-信号集

信号是unix和linux系統響應某些條件而産生的一個事件,接收到該信号的程序會相應地采取一些行動。通常信号是由一個錯誤産生的。但它們還可以作為程序間通信或修改行為的一種方式,明确地由一個程序發送給另一個程序。一個信号的産生叫生成,接收到一個信号叫捕獲。

信号是在軟體層次上對中斷機制的一種模拟,在原理上,一個程序收到一個信号與處理器收到一個中斷請求可以說是一樣的。

信号是異步的,一個程序不必通過任何操作來等待信号的到達,事實上,程序也不知道信号到底什麼時候到達。

信号是程序間通信機制中唯一的異步通信機制,可以看作是異步通知,通知接收信号的程序有哪些事情發生了。信号機制經過posix實時擴充後,功能更加強大,除了基本通知功能外,還可以傳遞附加資訊。

信号事件的發生有兩個來源

硬體來源(比如我們按下了鍵盤或者其它硬體故障);

軟體來源,最常用發送信号的系統函數是kill, raise, alarm和setitimer以及sigqueue函數,軟體來源還包括一些非法運算等操作。

信号可以直接進行使用者空間程序和核心程序之間的互動,核心程序也可以利用它來通知使用者空間程序發生了那些系統事件。

如果該程序目前并未處于執行态,則該信号就由核心儲存起來,直到該程序恢複執行再傳遞個它;如果一個信号被程序設定為阻塞,則該信号的傳遞被延遲,直到其阻塞取消時才被傳遞給程序。

當使用者按某些終端鍵時,将産生信号。

終端上按“ctrl+c”組合鍵通常産生中斷信号 sigint,終端上按“ctrl+\”鍵通常産生中斷信号 sigquit,終端上按“ctrl+z”鍵通常産生中斷信号 sigstop 等。

硬體異常将産生信号。

比如資料運算時,除數為0;或者無效的存放通路等.這些條件通常由硬體檢測到,并通知核心,然後核心為該條件發生時正在運作的程序産生适當的信号.。

軟體異常将産生信号。

當檢測到某種軟體條件已發生,并将其通知有關程序時,産生信号。

調用 kill() 函數将發送信号。

注意:接收信号程序和發送信号程序的所有者必須相同,或發送信号程序的所有者必須是超級使用者。

運作 kill 指令将發送信号。

此程式實際上是使用 kill 函數來發送信号。也常用此指令終止一個失控的背景程序。

若核心(空間)向使用者空間(程序)發出某個信号時,使用者空間(程序)可按照下列3中方式來面對:

忽略信号,即對信号不做任何處理

大多數信号都可以使用這種方式處理,但信号sigkill和sigstop絕不能被忽略.因為它們向超級使用者提供了一種使程序終止的可靠方法.

預設動作,執行信号的預設動作.大多數信号的系統預設動作是終止在程序.

捕捉信号,定義信号處理函數,當信号發生時,執行相應的處理函數;

注意,程序對實時信号的預設反應是程序終止。

linux究竟采用上述三種方式的哪一個來響應信号,取決于傳遞給相應api函數的參數。

信号是一種軟體中斷機制,即當信号發生時,必須用中斷的方法告訴核心”請執行下列操作”.

在linux終端内輸入<code>kill -l</code>可以檢視系統所支援的信号.可以看出,每個信号的名字都是以sig開頭.

程式間通信之-信号signal--linux核心剖析(九) 信号及信号來源 linux信号的發展及種類 信号的發送 信号的安裝(設定信号關關聯作) 信号進階-信号集

在頭檔案signal.h(/usr/include/bits/signum.h)中,這些信号都被定義為正整數,即每個信号和一個數字編碼相對應.

我的位于<code>/usr/include/x86_64-linux-gnu/bits/signum.h</code>

程式間通信之-信号signal--linux核心剖析(九) 信号及信号來源 linux信号的發展及種類 信号的發送 信号的安裝(設定信号關關聯作) 信号進階-信号集

其中sigrtmin,sigrtmax定義如下

1

2

3

4

5

6

7

程式間通信之-信号signal--linux核心剖析(九) 信号及信号來源 linux信号的發展及種類 信号的發送 信号的安裝(設定信号關關聯作) 信号進階-信号集

可以從兩個不同的分類角度對信号進行分類:

可靠性方面:可靠信号與不可靠信号;

與時間的關系上:實時信号與非實時信号。

linux信号機制基本上是從unix系統中繼承過來的。早期unix系統中的信号機制比較簡單和原始,後來在實踐中暴露出一些問題,是以,把那些建立在早期機制上的信号叫做”不可靠信号”,信号值小于sigrtmin(red hat 7.2中,sigrtmin=32,sigrtmax=63)的信号都是不可靠信号。這就是”不可靠信号”的來源。

它的主要問題是:

程序每次處理信号後,就将對信号的響應設定為預設動作。在某些情況下,将導緻對信号的錯誤處理;

是以,使用者如果不希望這樣的操作,那麼就要在信号處理函數結尾再一次調用signal(),重新安裝該信号。

信号可能丢失,後面将對此詳細闡述。

是以,早期unix下的不可靠信号主要指的是程序可能對信号做出錯誤的反應以及信号可能丢失。

linux支援不可靠信号,但是對不可靠信号機制做了改進:在調用完信号處理函數後,不必重新調用該信号的安裝函數(信号安裝函數是在可靠機制上的實作)。是以,linux下的不可靠信号問題主要指的是信号可能丢失。

随着時間的發展,實踐證明了有必要對信号的原始機制加以改進和擴充。是以,後來出現的各種unix版本分别在這方面進行了研究,力圖實作”可靠信号”。由于原來定義的信号已有許多應用,不好再做改動,最終隻好又新增加了一些信号,并在一開始就把它們定義為可靠信号,這些信号支援排隊,不會丢失。

同時,信号的發送和安裝也出現了新版本:信号發送函數sigqueue()及信号安裝函數sigaction()。

posix.4對可靠信号機制做了标準化。但是,posix隻對可靠信号機制應具有的功能以及信号機制的對外接口做了标準化,對信号機制的實作沒有作具體的規定。

信号值位于sigrtmin和sigrtmax之間的信号都是可靠信号,可靠信号克服了信号可能丢失的問題。

linux在支援新版本的信号安裝函數sigation()以及信号發送函數sigqueue()的同時,仍然支援早期的signal()信号安裝函數,支援信号發送函數kill()。

注: 不要有這樣的誤解:由sigqueue()發送、sigaction安裝的信号就是可靠的。

事實上,可靠信号是指後來添加的新信号(信号值位于sigrtmin及sigrtmax之間);不可靠信号是信号值小于sigrtmin的信号。

信号的可靠與不可靠隻與信号值有關,與信号的發送及安裝函數無關。目前linux中的signal()是通過sigation()函數實作的,是以,即使通過signal()安裝的信号,在信号處理函數的結尾也不必再調用一次信号安裝函數。同時,由signal()安裝的實時信号支援排隊,同樣不會丢失。

對于目前linux的兩個信号安裝函數:signal()及sigaction()來說,它們都不能把sigrtmin以前的信号變成可靠信号(都不支援排隊,仍有可能丢失,仍然是不可靠信号),而且對sigrtmin以後的信号都支援排隊。這兩個函數的最大差別在于,經過sigaction安裝的信号都能傳遞資訊給信号處理函數(對所有信号這一點都成立),而經過signal安裝的信号卻不能向信号處理函數傳遞資訊。對于信号發送函數來說也是一樣的。

早期unix系統隻定義了32種信号,ret hat7.2支援64種信号,編号0-63(sigrtmin=31,sigrtmax=63),将來可能進一步增加,這需要得到核心的支援。

前32種信号已經有了預定義值,每個信号有了确定的用途及含義,并且每種信号都有各自的預設動作。如按鍵盤的ctrl ^c時,會産生sigint信号,對該信号的預設反應就是程序終止。後32個信号表示實時信号,等同于前面闡述的可靠信号。這保證了發送的多個實時信号都被接收。實時信号是posix标準的一部分,可用于應用程序。

非實時信号都不支援排隊,都是不可靠信号;實時信号都支援排隊,都是可靠信号。

發送信号的主要函數有:<code>kill()</code>、<code>raise()</code>、<code>sigqueue()</code>、<code>alarm()</code>、<code>setitimer()</code>以及<code>abort()</code>。

使用man 2 kill檢視幫助資訊

程式間通信之-信号signal--linux核心剖析(九) 信号及信号來源 linux信号的發展及種類 信号的發送 信号的安裝(設定信号關關聯作) 信号進階-信号集

函數原型

參數說明

第一個參數pid:指定發送信号的接收線程

第二個參數signo:信号的signum

參數pid

參數pid的值

信号的接收程序

pid&gt;0

程序id為pid的程序

pid=0

同一個程序組的程序

pid&lt;0 pid!=-1

程序組id為 -pid的所有程序

pid=-1

除發送程序自身外,所有程序id大于1的程序

參數signo

signo是信号值,當為0時(即空信号),實際不發送任何信号,但照常進行錯誤檢查,是以,可用于檢查目标程序是否存在,以及目前程序是否具有向目标發送信号的權限(root權限的程序可以向任何程序發送信号,非root權限的程序隻能向屬于同一個session或者同一個使用者的程序發送信号)。

kill()最常用于pid&gt;0時的信号發送,調用成功傳回 0; 否則,傳回 -1。

注 對于pid&lt;0時的情況,對于哪些程序将接受信号,各種版本說法不一,其實很簡單,參閱核心源碼kernal/signal.c即可

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

程式間通信之-信号signal--linux核心剖析(九) 信号及信号來源 linux信号的發展及種類 信号的發送 信号的安裝(設定信号關關聯作) 信号進階-信号集

在下面程式中,來父子程序各自每隔一秒列印一句話,3 秒後,父程序通過 kill() 函數給子程序發送一個中斷信号 sigint( 2 号信号),最終,子程序結束,剩下父程序在列印資訊

48

49

50

程式間通信之-信号signal--linux核心剖析(九) 信号及信号來源 linux信号的發展及種類 信号的發送 信号的安裝(設定信号關關聯作) 信号進階-信号集

向程序本身發送信号,參數為即将發送的信号值。

調用成功傳回 0;否則,傳回 -1。

kill和raise有如下等價關系:

<code>kill(getpid(), xxx)</code>等價于<code>raise(xxx)</code>, 意思是, raise函數就是向目前程序發信号的。

程式間通信之-信号signal--linux核心剖析(九) 信号及信号來源 linux信号的發展及種類 信号的發送 信号的安裝(設定信号關關聯作) 信号進階-信号集

我們下面的程式,程序通過raise向自身發送了一個sigint信号。

在linux的64個信号中,大多數在預設情況下都是終止目前信号.包括sigint,當到了定時時間後,核心發出sigint信号,該信号會終止目前程序.

程式間通信之-信号signal--linux核心剖析(九) 信号及信号來源 linux信号的發展及種類 信号的發送 信号的安裝(設定信号關關聯作) 信号進階-信号集

專門為sigalrm信号而設,在指定的時間seconds秒後,将向程序本身發送sigalrm信号,又稱為鬧鐘時間。

程序調用alarm後,任何以前的alarm()調用都将無效。如果參數seconds為零,那麼程序内将不再包含任何鬧鐘時間。

傳回值,如果調用alarm()前,程序中已經設定了鬧鐘時間,則傳回上一個鬧鐘時間的剩餘時間,否則傳回0。

在linux下如果對定時要求不太精确的話,使用alarm()和signal()就行了,但是如果想要實作精度較高的定時功能的話,就要使用setitimer函數。

程式間通信之-信号signal--linux核心剖析(九) 信号及信号來源 linux信号的發展及種類 信号的發送 信号的安裝(設定信号關關聯作) 信号進階-信号集

<code>setitimer()</code>比<code>alarm</code>功能強大,支援3種類型的定時器:

定時器

描述

itimer_real

設定絕對時間;經過指定的時間後,核心将發送sigalrm信号給本程序;

itimer_virtual

設定程式執行時間;經過指定的時間後,核心将發送sigvtalrm信号給本程序;

itimer_prof

設定程序執行以及核心因本程序而消耗的時間和,經過指定的時間後,核心将發送itimer_virtual信号給本程序;

* 第一個參數which指定定時器類型(上面三種之一);

第二個參數是結構itimerval的一個執行個體,結構itimerval形式見附錄1。

第三個參數可不做處理。

setitimer()調用成功傳回0,否則傳回-1。

程式間通信之-信号signal--linux核心剖析(九) 信号及信号來源 linux信号的發展及種類 信号的發送 信号的安裝(設定信号關關聯作) 信号進階-信号集
程式間通信之-信号signal--linux核心剖析(九) 信号及信号來源 linux信号的發展及種類 信号的發送 信号的安裝(設定信号關關聯作) 信号進階-信号集

通過pause可以十目前程序挂起,直至信号出現。

在我們下面的例子中,系統在延遲3s後列印輸出”i am a father process,i will send signal now”,然後結束目前程序.

注意,程式并不會列印輸出”hello i am child process”.

程式間通信之-信号signal--linux核心剖析(九) 信号及信号來源 linux信号的發展及種類 信号的發送 信号的安裝(設定信号關關聯作) 信号進階-信号集

向程序發送sigabort信号,預設情況下程序會異常退出,當然可定義自己的信号處理函數。即使sigabort被程序設定為阻塞信号,調用abort()後,sigabort仍然能被程序接收。該函數無傳回值。

在隊列中向指定程序發送一個信号和資料。

之前學過kill,raise,alarm,abort等功能稍簡單的信号發送函數,現在我們學習一種新的功能比較強大的信号發送函數sigqueue.

程式間通信之-信号signal--linux核心剖析(九) 信号及信号來源 linux信号的發展及種類 信号的發送 信号的安裝(設定信号關關聯作) 信号進階-信号集

sigqueue()是比較新的發送信号系統調用,主要是針對實時信号提出的(當然也支援前32種),支援信号帶有參數,與函數sigaction()配合使用。

第一個參數是指定接收信号的程序id,

第二個參數确定即将發送的信号,

第三個參數是一個聯合資料結構union sigval,指定了信号傳遞的參數,即通常所說的4位元組值。

sigqueue()比kill()傳遞了更多的附加資訊,但sigqueue()隻能向一個程序發送信号,而不能發送信号給一個程序組。

如果signo=0,将會執行錯誤檢查,但實際上不發送任何信号,0值信号可用于檢查pid的有效性以及目前程序是否有權限向目标程序發送信号。

在調用sigqueue時,sigval_t指定的資訊會拷貝到參數信号處理函數(參數信号處理函數指的是信号處理函數由sigaction安裝,并設定了sa_sigaction指針)的siginfo_t結構中,這樣信号處理函數就可以處理這些資訊了。

由于sigqueue系統調用支援發送帶參數信号,是以比kill()系統調用的功能要靈活和強大得多。

sigqueue()發送非實時信号時,第三個參數包含的資訊仍然能夠傳遞給信号處理函數; sigqueue()發送非實時信号時,仍然不支援排隊,即在信号處理函數執行過程中到來的所有相同信号,都被合并為一個信号。

如果程序要處理某一信号,那麼就要在程序中安裝該信号。

安裝信号主要用來确定信号值及程序針對該信号值的動作之間的映射關系,

即程序将要處理哪個信号;該信号被傳遞給程序時,将執行何種操作。

linux主要有兩個函數實作信号的安裝:<code>signal()</code>、<code>sigaction()</code>。

其中signal()在可靠信号系統調用的基礎上實作, 是庫函數。它隻有兩個參數,不支援信号傳遞資訊,主要是用于前32種非實時信号的安裝;

而sigaction()是較新的函數(由兩個系統調用實作:<code>sys_signal</code>以及<code>sys_rt_sigaction</code>),有三個參數,支援信号傳遞資訊,主要用來與 <code>sigqueue()</code> 系統調用配合使用,當然,<code>sigaction()</code>同樣支援非實時信号的安裝。sigaction()優于signal()主要展現在支援信号帶有參數。

如果該函數原型不容易了解的話,可以參考下面的分解方式來了解:

第一個參數指定信号的值,第二個參數指定針對前面信号值的處理,可以忽略該信号(參數設為sig_ign);可以采用系統預設方式處理信号(參數設為sig_dfl);也可以自己實作處理方式(參數指定一個函數位址)。

如果signal()調用成功,傳回最後一次為安裝信号signum而調用signal()時的handler值;失敗則傳回sig_err。

例如之前的setitimer精确定時器信号,作業系統的預設處理是終止程序,那麼現在我們就可以自己編寫信号處理函數,然後通過signal來安裝。

51

52

53

54

55

56

57

58

59

60

61

62

sigaction函數用于改變程序接收到特定信号後的行為。

該函數的第一個參數為信号的值,可以為除sigkill及sigstop外的任何一個特定有效的信号(為這兩個信号定義自己的處理函數,将導緻信号安裝錯誤)。

第二個參數是指向結構sigaction的一個執行個體的指針,在結構sigaction的執行個體中,指定了對特定信号的處理,可以為空,程序會以預設方式對信号處理;

第三個參數oldact指向的對象用來儲存原來對相應信号的處理,可指定oldact為null。

如果把第二、第三個參數都設為null,那麼該函數可用于檢查信号的有效性。

第二個參數最為重要,其中包含了對指定信号的處理、信号所傳遞的資訊、信号處理函數執行過程中應屏蔽掉哪些函數等等。

sigaction結構定義如下:

其中,sa_restorer,已過時,posix不支援它,不應再被使用。

聯合資料結構中的兩個元素_sa_handler以及*_sa_sigaction指定信号關聯函數,即使用者指定的信号處理函數。除了可以是使用者自定義的處理函數外,還可以為sig_dfl(采用預設的處理方式),也可以為sig_ign(忽略信号)。

由_sa_handler指定的處理函數隻有一個參數,即信号值,是以信号不能傳遞除信号值之外的任何資訊;由_sa_sigaction是指定的信号處理函數帶有三個參數,是為實時信号而設的(當然同樣支援非實時信号),它指定一個3參數信号處理函數。第一個參數為信号值,第三個參數沒有使用(posix沒有規範使用該參數的标準),第二個參數是指向siginfo_t結構的指針,結構中包含信号攜帶的資料值,參數所指向的結構如下:

siginfo_t結構中的聯合資料成員確定該結構适應所有的信号,比如對于實時信号來說,則實際采用下面的結構形式:

結構的第四個域同樣為一個聯合資料結構:

采用聯合資料結構,說明siginfo_t結構中的si_value要麼持有一個4位元組的整數值,要麼持有一個指針,這就構成了與信号相關的資料。在信号的處理函數中,包含這樣的信号相關資料指針,但沒有規定具體如何對這些資料進行操作,操作方法應該由程式開發人員根據具體任務事先約定。

前面在讨論系統調用sigqueue發送信号時,sigqueue的第三個參數就是sigval聯合資料結構,當調用sigqueue時,該資料結構中的資料就将拷貝到信号處理函數的第二個參數中。這樣,在發送信号同時,就可以讓信号傳遞一些附加資訊。信号可以傳遞資訊對程式開發是非常有意義的。

sa_mask指定在信号處理程式執行過程中,哪些信号應當被阻塞。預設情況下目前信号本身被阻塞,防止信号的嵌套發送,除非指定sa_nodefer或者sa_nomask标志位。

注:請注意sa_mask指定的信号阻塞的前提條件,是在由sigaction()安裝信号的處理函數執行過程中由sa_mask指定的信号才被阻塞。

sa_flags中包含了許多标志位,包括剛剛提到的sa_nodefer及sa_nomask标志位。另一個比較重要的标志位是sa_siginfo,當設定了該标志位時,表示信号附帶的參數可以被傳遞到信号處理函數中,是以,應該為sigaction結構中的sa_sigaction指定處理函數,而不應該為sa_handler指定信号處理函數,否則,設定該标志變得毫無意義。即使為sa_sigaction指定了信号處理函數,如果不設定sa_siginfo,信号處理函數同樣不能得到信号傳遞過來的資料,在信号處理函數中對這些資訊的通路都将導緻段錯誤(segmentation fault)。

程式間通信之-信号signal--linux核心剖析(九) 信号及信号來源 linux信号的發展及種類 信号的發送 信号的安裝(設定信号關關聯作) 信号進階-信号集

實際上signal底層實作就是利用sigaction

程式間通信之-信号signal--linux核心剖析(九) 信号及信号來源 linux信号的發展及種類 信号的發送 信号的安裝(設定信号關關聯作) 信号進階-信号集

可知,安裝信号sigint時,将sigquit加入到sa_mask阻塞集中,則當sigint信号正在執行處理函數時,sigquit信号将被阻塞,隻有當sigint信号處理函數執行完後才解除對sigquit信号的阻塞,由于sigquit是不可靠信号,不支援排隊,是以隻遞達一次

程式間通信之-信号signal--linux核心剖析(九) 信号及信号來源 linux信号的發展及種類 信号的發送 信号的安裝(設定信号關關聯作) 信号進階-信号集
程式間通信之-信号signal--linux核心剖析(九) 信号及信号來源 linux信号的發展及種類 信号的發送 信号的安裝(設定信号關關聯作) 信号進階-信号集

發送端

接收端

程式間通信之-信号signal--linux核心剖析(九) 信号及信号來源 linux信号的發展及種類 信号的發送 信号的安裝(設定信号關關聯作) 信号進階-信号集

從信号發送到信号處理函數的執行完畢

對于一個完整的信号生命周期(從信号發送到相應的處理函數執行完畢)來說,可以分為三個重要的階段,這三個階段由四個重要事件來刻畫:

信号誕生;

信号在程序中注冊完畢;

信号在程序中的登出完畢;

信号處理函數執行完畢。

相鄰兩個事件的時間間隔構成信号生命周期的一個階段。

程式間通信之-信号signal--linux核心剖析(九) 信号及信号來源 linux信号的發展及種類 信号的發送 信号的安裝(設定信号關關聯作) 信号進階-信号集

下面闡述四個事件的實際意義:

信号的誕生指的是觸發信号的事件發生(如檢測到硬體異常、定時器逾時以及調用信号發送函數kill()或sigqueue()等)。

信号在目标程序中”注冊”;程序的task_struct結構中有關于本程序中未決信号的資料成員struct sigpending pending

第三個成員是程序中所有未決信号集,第一、第二個成員分别指向一個sigqueue類型的結構鍊(稱之為”未決信号資訊鍊”)的首尾,資訊鍊中的每個sigqueue結構刻畫一個特定信号所攜帶的資訊,并指向下一個sigqueue結構:

信号在程序中注冊指的就是信号值加入到程序的未決信号集中(sigpending結構的第二個成員sigset_t signal),并且信号所攜帶的資訊被保留到未決信号資訊鍊的某個sigqueue結構中。 隻要信号在程序的未決信号集中,表明程序已經知道這些信号的存在,但還沒來得及處理,或者該信号被程序阻塞。

當一個實時信号發送給一個程序時,不管該信号是否已經在程序中注冊,都會被再注冊一次,是以,信号不會丢失,是以,實時信号又叫做”可靠信号”。這意味着同一個實時信号可以在同一個程序的未決信号資訊鍊中占有多個sigqueue結構(程序每收到一個實時信号,都會為它配置設定一個結構來登記該信号資訊,并把該結構添加在未決信号鍊尾,即所有誕生的實時信号都會在目标程序中注冊);

當一個非實時信号發送給一個程序時,如果該信号已經在程序中注冊,則該信号将被丢棄,造成信号丢失。

是以,非實時信号又叫做”不可靠信号”。

這意味着同一個非實時信号在程序的未決信号資訊鍊中,至多占有一個sigqueue結構

一個非實時信号誕生後,

如果發現相同的信号已經在目标結構中注冊,則不再注冊,對于程序來說,相當于不知道本次信号發生,信号丢失;

如果程序的未決信号中沒有相同信号,則在程序中注冊自己)。

在目标程序執行過程中,會檢測是否有信号等待處理(每次從系統空間傳回到使用者空間時都做這樣的檢查)。

如果存在未決信号等待處理且該信号沒有被程序阻塞,則在運作相應的信号處理函數前,程序會把信号在未決信号鍊中占有的結構卸掉。是否将信号從程序未決信号集中删除對于實時與非實時信号是不同的。

對于非實時信号來說,由于在未決信号資訊鍊中最多隻占用一個sigqueue結構,是以該結構被釋放後,應該把信号在程序未決信号集中删除(信号登出完畢);

而對于實時信号來說,可能在未決信号資訊鍊中占用多個sigqueue結構,是以應該針對占用sigqueue結構的數目差別對待:

如果隻占用一個sigqueue結構(程序隻收到該信号一次),則應該把信号在程序的未決信号集中删除(信号登出完畢)。

否則,不應該在程序的未決信号集中删除該信号(信号登出完畢)。

程序在執行信号相應處理函數之前,首先要把信号在程序中登出。

程序登出信号後,立即執行相應的信号處理函數,執行完畢後,信号的本次發送對程序的影響徹底結束。

信号注冊與否,與發送信号的函數(如kill()或sigqueue()等)以及信号安裝函數(signal()及sigaction())無關,隻與信号值有關(信号值小于sigrtmin的信号最多隻注冊一次,信号值在sigrtmin及sigrtmax之間的信号,隻要被程序接收到就被注冊)。 在信号被登出到相應的信号處理函數執行完畢這段時間内,如果程序又收到同一信号多次,則對實時信号來說,每一次都會在程序中注冊;而對于非實時信号來說,無論收到多少次信号,都會視為隻收到一個信号,隻在程序中注冊一次。

信号源為目标程序産生了一個信号,然後由核心來決定是否要将該信号傳遞給目标程序。從信号産生到傳遞給目标程序的流程圖如

程式間通信之-信号signal--linux核心剖析(九) 信号及信号來源 linux信号的發展及種類 信号的發送 信号的安裝(設定信号關關聯作) 信号進階-信号集

程序可以阻塞信号的傳遞。當信号源為目标程序産生了一個信号之後,核心會執行依次執行下面操作,

如果目标程序設定了忽略該信号,則核心直接将該信号丢棄。

如果目标程序沒有阻塞該信号,則核心将該信号傳遞給目标程序,由目标程序執行相對應操作。

如果目标程序設定阻塞該信号,則核心将該信号放到目标程序的阻塞信号清單中,等待目标程序對該類型信号的下一步設定。

若目标程序後續設定忽略該信号,則核心将該信号從目标程序的阻塞信号清單中移除并丢棄。若目标程序對該信号解除了阻塞,核心将該信号傳遞給目标程序進行相對應的操作。

在信号産生到信号傳遞給目标程序之間的時間間隔内,我們稱該信号為未決的(pending)。

每個程序都有一個信号屏蔽字(signal mask),它規定了目前要阻塞傳遞給該程序的信号集。對于每種可能的信号,信号屏蔽字中都有一位與之對應。

我們已經知道,通過信号實作程式之間的互相通信,我們可以實作如下功能

可以通過信号來終止程序

可以通過信号來在程序間進行通信

程式通過指定信号的關聯處理函數來改變信号的預設處理方式

可以通過屏蔽某些信号,使其不能傳遞給程序。

那麼我們應該如何設定我們需要處理的信号,我們不需要處理哪些信号等問題呢?

信号集函數就是幫助我們解決這些問題的。

信号集被定義為一種資料類型

信号集用來描述信号的集合,linux所支援的所有信号可以全部或部分的出現在信号集中,主要與信号阻塞相關函數配合使用。

posix.1 定義了一個資料類型sigset_t,用于表示信号集。

另外,頭檔案 signal.h 提供了下列五個處理信号集的函數。

函數

功能

sigemptyset(sigset_t *set)

初始化由set指定的信号集,信号集裡面的所有信号被清空;

sigfillset(sigset_t *set)

調用該函數後,set指向的信号集中将包含linux支援的64種信号;

sigaddset(sigset_t *set, int signum)

在set指向的信号集中加入signum信号;

sigdelset(sigset_t *set, int signum)

在set指向的信号集中删除signum信号;

sigismember(const sigset_t *set, int signum)

判定信号signum是否在set指向的信号集中。

函數 sigemptyset 初始化由 set 指向的信号集,清除其中所有信号。

傳回值:若成功則傳回0,若出錯則傳回-1

函數 sigfillset 初始化由 set 指向的信号集,使其包含所有信号。

函數 sigaddset 将一個信号 signo 添加到現有信号集 set 中。

函數 sigdelset 将一個信号 signo 從信号集 set 中删除。

函數 sigismember 判斷指定信号 signo 是否在信号集 set 中。

傳回值:若真則傳回1,若假則傳回0,若出錯則傳回-1

每個程序都有一個用來描述哪些信号遞送到程序時将被阻塞的信号集,該信号集中的所有信号在遞送到程序後都将被阻塞。

下面是與信号阻塞相關的幾個函數:

sigprocmask()函數能夠根據參數how來實作對信号集的操作,操作主要有三種:

參數how

程序目前信号集

sig_block

在程序目前阻塞信号集中添加set指向信号集中的信号

sig_unblock

如果程序阻塞信号集中包含set指向信号集中的信号,則解除對該信号的阻塞

sig_setmask

更新程序阻塞信号集為set指向的信号集

在下面的程式檔案中先調用 sigprocmask 設定阻塞信号 sigalrm,然後調用 alarm(2) 設定一個兩秒鐘的鬧鐘(兩秒鐘之後将向目前程序産生一個 sigalrm 信号)。在睡眠 4 秒鐘之後(此時應該已經産生了 sigalrm 信号),調用 sigprocmask 函數解除對信号sigalrm 的阻塞。

程式間通信之-信号signal--linux核心剖析(九) 信号及信号來源 linux信号的發展及種類 信号的發送 信号的安裝(設定信号關關聯作) 信号進階-信号集

從上面的執行輸出,我們看到信号 sigalrm 是在調用 sigprocmask函數執行 unblock之後才被傳遞給目前程序進行處理的。

程式間通信之-信号signal--linux核心剖析(九) 信号及信号來源 linux信号的發展及種類 信号的發送 信号的安裝(設定信号關關聯作) 信号進階-信号集

如果我們将代碼中的<code>sigprocemask(sig_block, &amp;sigset, null)</code> 注釋掉,編譯執行,生成如下結果

程式間通信之-信号signal--linux核心剖析(九) 信号及信号來源 linux信号的發展及種類 信号的發送 信号的安裝(設定信号關關聯作) 信号進階-信号集

我們看到由于沒有屏蔽信号 sigalrm ,程式在2秒後捕獲了sigalrm直接調用sig_alrm進行了處理。

函數 sigpending 獲得目前已遞送到程序,卻被阻塞的所有信号,在set指向的信号集中傳回結果。

程式間通信之-信号signal--linux核心剖析(九) 信号及信号來源 linux信号的發展及種類 信号的發送 信号的安裝(設定信号關關聯作) 信号進階-信号集

從運作結果,我們看到調用 alarm 函數産生信号 sigalrm 之後,該信号在 sigpending 函數的 set 參數指向的信号集中。

sigsuspend(const sigset_t *mask))用于在接收到某個信号之前, 臨時用mask替換程序的信号掩碼, 并暫停程序執行,直到收到信号為止。sigsuspend 傳回後将恢複調用之前的信号掩碼。信号處理函數完成後,程序将繼續執行。該系統調用始終傳回-1,并将errno設定為eintr。

如果一個信号被程序阻塞,它就不會傳遞給程序,但會停留在待處理狀态,當程序解除對待處理信号的阻塞時,待處理信号就會立刻被處理。

下面以一個例子來說明上述函數的用法,源檔案為sigset.c,代碼如下:

程式間通信之-信号signal--linux核心剖析(九) 信号及信号來源 linux信号的發展及種類 信号的發送 信号的安裝(設定信号關關聯作) 信号進階-信号集

轉載:http://blog.csdn.net/gatieme/article/details/50990456

繼續閱讀