[ULK11]信號(一):一些基本概念

進程在正常結束以前,不可能無休止地運行下去.進程在運行的時候,系統中總會發生這樣或那樣的事件.內核必須響應這些事件,這樣,進程的執行就會被打斷.

一.從那些干擾程序執行的事件說起

有些事件產生自硬件,比如時鐘或I/O設備.這樣的事件通常與當前正在執行的進程沒有什麼關係,不會對進程的執行流造成任何影響.於是,進程根本意識不到自己曾被這種事件打斷過.我們通常將這樣的事件叫做中斷.

有些事件來自進程自身,這通常意味着進程做了些不該做的事,比如除0.當這樣的事情發生時,內核必須打斷正在執行的進程,並進行相應的處理.這樣的事件幾乎一定會改變進程的執行流.我們通常把這樣的事件叫做異常.

還有些事件既不是從硬件產生,也不是從進程自身產生.這樣的事件產生自其他進程.Linux允許一個進程向另一個進程發送一個消息,以便進程之間可以相互”通信”.於是,爲了響應消息,收到消息的進程通常又會被打斷.我們把這樣的事件叫做信號.

現實更加複雜.事實上,異常事件發生以後,內核不會強勢介入進程的執行流,而是用了一種更加”文雅”的方式:向進程發送一個信號,雖然在大多數情況下這個信號會殺死進程.

二.信號

這樣,我們知道,信號是被髮送給進程的一條消息,收到信號的進程通常會對信號作出響應,從而改變自己的執行流.

並且,我們還可以總結出信號的兩個作用:

  • 在用戶態, 信號被用作進程間通信;
  • 在內核態,信號被內核用來通知進程已發生的事件.

我們需要先定義一些與信號處理相關的術語,並明確它們的語義.

三.信號的三個狀態:產生,傳遞,未決

首先,我們來定義信號的三個狀態.值得注意的是,信號的三個狀態存在於理論之中,內核不會記錄信號的狀態.

  • 產生
    當一個導致信號生成的事件發生時,內核會根據需要更新一個或多個進程描述符.從事件發生到進程描述符更新完畢,這個過程叫做信號產生.下面這些事件都可能引起信號產生:
    • 硬件異常(如除0)
    • 軟件條件(如定時器超時)
    • 終端操作
    • kill()函數調用
  • 傳遞
    內核強迫目標進程對信號作出反應.這個過程叫做信號傳遞.內核強迫目標進程相應信號的方式有:
    • 改變目標進程的執行狀態
    • 執行特定的信號處理程序
    • 既改變目標進程的執行狀態,又執行特定的信號處理程序
  • 未決
    產生的信號不會立即被傳遞.當信號已產生但是沒有被傳遞時,信號處於未決狀態.

四.信號的傳遞方式:忽略,默認,捕捉

其次, 我們看內核傳遞信號的三種方式.與信號狀態不同的是,內核對信號的處理動作明確記錄在進程描述符的sighand->action[sig].sa_handler字段中.

  • 顯式忽略: SIG_IGN
  • 默認操作: SIG_DFL
    默認操作與信號相關,有以下三種:
    • Terminate: 終止進程
    • Dump: 終止進程並轉儲其執行上下文
    • Ignore: 忽略信號
    • Stop: 停止進程
    • Continue: 若進程被停止,就將它設爲運行態
  • 捕獲: handler_address

至此,事情好像已經完美了: 信號產生以後,經歷未決狀態,最後被內核根據註冊在進程描述符中的動作進行傳遞.然而,信號並不總是能夠被順利傳遞,因爲內核可能阻塞信號.

五.信號的阻塞

  • 阻塞
    信號註冊在以下兩個地方時,信號被阻塞.被阻塞的信號處在信號未決狀態,當它被解除阻塞以後,它纔有機會被傳遞.
    • blocked
    • sighand->action[sig]->sa_mask
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章