信號與進程是分不開的,而把信號與進程的筆記分開來寫,是因為我覺得這個信號太難搞懂了,特別是APUE信號這一章還把信號結合歷史來介紹弄的我云里霧里。
信號本質上是在軟件層次上對中斷機制的一種模擬,他有幾種產(chǎn)生方式和處理方式(APUE有介紹),下面帶著疑惑從幾個角度對信號進行介紹
(一) 站在進程的角度
進程發(fā)現(xiàn)和接受信號
我們知道,信號是異步的,一個進程不可能等待信號的到來,也不知道信號會到來,那么,進程是如何發(fā)現(xiàn)和接受信號呢?實際上,信號的接收不是由用戶進程來完成的,而是由內(nèi)核代理。當一個進程P2向另一個進程P1發(fā)送信號后,內(nèi)核接受到信號,并將其放在P1的信號隊列當中。當P1再次陷入內(nèi)核態(tài)時,會檢查信號隊列,并根據(jù)相應的信號調(diào)取相應的信號處理函數(shù)
Task _struct 是進程控制塊(PCB),詳見 http://oss.org.cn/kernel-book/ch04/4.3.htm

信號檢測和響應時機
剛才我們說,當P1再次陷入內(nèi)核時,會檢查信號隊列。那么,P1什么時候會再次陷入內(nèi)核呢?陷入內(nèi)核后在什么時機會檢測信號隊列呢?
1. 當前進程由于系統(tǒng)調(diào)用、中斷或異常而進入系統(tǒng)空間以后,從系統(tǒng)空間返回到用戶空間的前夕。
2. 當前進程在內(nèi)核中進入睡眠以后剛被喚醒的時候(必定是在系統(tǒng)調(diào)用中),或者由于不可忽略信號的存在而提前返回到用戶空間
進入信號處理函數(shù)

對于sigprocmask 會進入內(nèi)核空間、pause需要從進入睡眠這兩者都符合檢測處理信號函數(shù)的條件,所以存在忽略信號的情況,而APUE講sigsuspend的之后真是晦澀難懂,其實他主要做的工作就是等待一個中斷然后執(zhí)行相應的handle處理
所以我感覺例子中的
sigsuspend(&zeromask);
sigprocmask(SIG_SETMASK, &oldmask,NULL);
是不是可以直接替換為
Sigsuspend(&oldmask)
因為測試情況難以出現(xiàn),這里只是個人理解并未得到驗證
(二) 站在信號自身的角度
信號生命周期:
對于一個完整的信號生命周期(從信號發(fā)送到相應的處理函數(shù)執(zhí)行完畢)來說,可以分為三個重要的階段,這三個階段由四個重要事件來刻畫:
1.信號誕生;
2.信號在進程中注冊完畢;
3.信號在進程中的注銷完畢;
4.信號處理函數(shù)執(zhí)行完畢。
相鄰兩個事件的時間間隔構成信號生命周期的一個階段。

詳細描述各個生命周期
1. 信號"誕生"。
信號的誕生指的是觸發(fā)信號的事件發(fā)生(如檢測到硬件異常、定時器超時以及調(diào)用信號發(fā)送函數(shù)kill()或sigqueue()等)。
2. 信號在目標進程中"注冊"。
這里注冊定義不是由signal或者sigaction實現(xiàn)的,而是說信號發(fā)生之后內(nèi)核中自動對信號的注冊保存。
進程的task_struct結構中有關于本進程中未決信號的數(shù)據(jù)成員:
struct sigpending pending;
struct sigpending
{
struct sigqueue *head, **tail;
sigset_t signal;
};
第一、第二個成員分別指向一個sigqueue類型的結構鏈(稱之為"未決信號信息鏈")的首尾,第三個成員是進程中所有未決信號集,信息鏈中的每個sigqueue結構體刻畫一個特定信號所攜帶的信息,并指向下一個sigqueue結構:
struct sigqueue
{
struct sigqueue *next;
siginfo_t info;
};
信號在進程中注冊指的就是信號值加入到進程的未決信號集中(sigpending結構的第二個成員sigset_t signal),并且信號所攜帶的信息被保留到未決信號信息鏈的某個sigqueue結構中。只要信號在進程的未決信號集中,表明進程已經(jīng)知道這些信號的存在,但還沒來得及處理,或者該信號被進程阻塞。
注:
當一個實時信號發(fā)送給一個進程時,不管該信號是否已經(jīng)在進程中注冊,都會被再注冊一次,因此,信號不會丟失,因此,實時信號又叫做"可靠信號"。這意味著同一個實時信號可以在同一個進程的未決信號信息鏈中占有多個sigqueue結構(進程每收到一個實時信號,都會為它分配一個結構來登記該信號信息,并把該結構添加在未決信號鏈尾,即所有誕生的實時信號都會在目標進程中注冊);
當一個非實時信號發(fā)送給一個進程時,如果該信號已經(jīng)在進程中注冊,則該信號將被丟棄,造成信號丟失。因此,非實時信號又叫做"不可靠信號"。這意味著同一個非實時信號在進程的未決信號信息鏈中,至多占有一個sigqueue結構。一個非實時信號誕生后,(1)、如果發(fā)現(xiàn)相同的信號已經(jīng)在目標結構中注冊,則不再注冊,對于進程來說,相當于不知道本次信號發(fā)生,信號丟失;(2)、如果進程的未決信號中沒有相同信號,則在進程中注冊自己。在APUE的不可靠信號章節(jié)中需要每次重新聲明sinal_hanle函數(shù),這個是說的以前Unix系統(tǒng)的處理,現(xiàn)在可靠不可靠就是上面所說的實時與注冊次數(shù)的區(qū)別。
3.信號在進程中的注銷。
在目標進程執(zhí)行過程中,會檢測是否有信號等待處理(每次從系統(tǒng)空間返回到用戶空間時都做這樣的檢查)。(“sigprocmask返回前,也至少會將其中一個未決且未阻塞的信號遞送給進程”)如果存在未決信號等待處理且該信號沒有被進程阻塞,則在運行相應的信號處理函數(shù)前,進程會把信號在未決信號鏈中占有的結構卸掉。是否將信號從進程未決信號集中刪除對于實時與非實時信號是不同的。對于非實時信號來說,由于在未決信號信息鏈中最多只占用一個sigqueue結構,因此該結構被釋放后,應該把信號在進程未決信號集中刪除(信號注銷完畢);而對于實時信號來說,可能在未決信號信息鏈中占用多個sigqueue結構,因此應該針對占用gqueue結構的數(shù)目區(qū)別對待:如果只占用一個sigqueue結構(進程只收到該信號一次),則應該把信號在進程的未決信號集中刪除(信號注銷完畢)。否則,不在進程的未決信號集中刪除該信號(信號注銷完畢)。進程在執(zhí)行信號相應處理函數(shù)之前,首先要把信號在進程中注銷。
4.信號生命終止。
進程注銷信號后,立即執(zhí)行相應的信號處理函數(shù),執(zhí)行完畢后,信號的本次發(fā)送對進程的影響徹底結束。
注:
1)信號注冊與否,與發(fā)送信號的函數(shù)(如kill()或sigqueue()等)以及信號安裝函數(shù)(signal()及sigaction())無關,只與信號值有關(信號值小于SIGRTMIN的信號最多只注冊一次,信號值在SIGRTMIN及SIGRTMAX之間的信號,只要被進程接收到就被注冊)。
2)在信號被注銷到相應的信號處理函數(shù)執(zhí)行完畢這段時間內(nèi),如果進程又收到同一信號多次,則對實時信號來說,每一次都會在進程中注冊;而對于非實時信號來說,無論收到多少次信號,都會視為只收到一個信號,只在進程中注冊一次。
(三) 進程和信號兩者的角度來看
實際執(zhí)行信號的處理動作稱為信號遞達(Delivery),信號從產(chǎn)生到遞達之間的狀態(tài),稱為信號未決(Pending)。進程可以選擇阻塞(Block)某個信號。被阻塞的信號產(chǎn)生時將保持在未決狀態(tài),直到進程解除對此信號的阻塞,才執(zhí)行遞達的動作。注意,阻塞和忽略是不同的,只要信號被阻塞就不會遞達,而忽略是在遞達之后可選的一種處理動作。信號在內(nèi)核中的表示可以看作是這樣的:

每個信號都有兩個標志位分別表示阻塞和未決,還有一個函數(shù)指針表示處理動作。信號產(chǎn)生時,內(nèi)核在進程控制塊中設置該信號的未決標志,直到信號遞達才清除該標志。在上圖的例子中,
1. SIGHUP信號未阻塞也未產(chǎn)生過,當它遞達時執(zhí)行默認處理動作。
2. SIGINT信號產(chǎn)生過,但正在被阻塞,所以暫時不能遞達。雖然它的處理動作是忽略,但在沒有解除阻塞之前不能忽略這個信號,因為進程仍有機會改變處理動作之后再解除阻塞。
3. SIGQUIT信號未產(chǎn)生過,一旦產(chǎn)生SIGQUIT信號將被阻塞,它的處理動作是用戶自定義函數(shù)sighandler。
posted on 2012-02-10 17:49
李陽 閱讀(652)
評論(1) 編輯 收藏 引用 所屬分類:
Linux