信號的相關概念

  • 信號的概念:信號機制是進程之間相互傳遞消息的一種方法,信號全稱爲軟中斷信號,也有人稱作軟中斷。
    軟中斷信號用來通知進程發生了異步事件,進程之間可以互相通過系統調用kill發送軟中斷信號。內核也可以因爲內部事件而給進程發送信號,通知進程發生了某個事件。注意,信號只是用來通知某進程發生了什麼事件,並不給進程傳遞任何數據。
    收到信號的進程對各種信號有不同的處理方法。具體方法在下文會有詳細的介紹。進程通過系統調用signal來指定進程對某個信號的處理行爲。
  • 產生信號有以下4種方式:
    1.用戶在終端按下某些鍵時,終端驅動程序會發送信號給前臺進程,例如:crtl+c產生SIGINT信號,ctrl+\產生SIGQUIT信號,crtl+z產生SIGTSTP信號(可使前臺進程停止)(一個命令後面加&可以放到後臺運行)
    2.硬件異常產生信號,這些條件由硬件檢測到並通知內核,然後內核向當前進程發送適當的信號。例如:當前進程執行了除以0的指令,CPU的運算單元會產生異常,內核將這個異常解釋爲SIGFPE信號發送給進程。再比如當進程訪問了非法內存地址,MMU會產生異常,內核將這個異常解釋爲SIGSEGV信號發送給進程
    3.一個進程調用kill(2)函數可以發送信號給另一個進程。可以用kill(1)命令發送信號給某個進程,kill(1)命令也是調用kill(2)函數實現的,如果不明確指定信號則發送SIGTERM信號,該信號的默認處理動作是終止進程,當該內核檢測到某種軟件條件發生時也可以通過信號通知進程。例如:鬧鐘超時產生SIGALRM信號,向讀端已關閉的管道寫數據時產生SIGPIPE信號。如果不想按默認動作處理信號,用戶程序可以調用sigaction(2)函數告訴內核如何處理某種信號。
    4.軟件條件產生。
  • 處理信號的常見方式有以下3種(忽略、默認、自定義):
    1.忽略此信號
    2.執行該信號的默認處理動作(部分進程默認狀態一般是終止進程)
    3.提供一個信號處理函數,要求內核在處理該信號時切換到用戶態執行這個處理函數,這種方式稱爲捕捉一個信號
  • 信號其他相關常見概念:
    實際執行信號的處理動作稱爲信號遞達(Delivery)
    信號從產生到遞達之間的狀態稱爲信號未決(Pending)
    進程可以選擇阻塞(Block)某個信號
    被阻塞的信號產生時將保持在未決狀態,直到進程解除對此信號的阻塞,才執行遞達的動作

    1.如果一個信號被Block一定不會被遞達
    2.如果一個信號沒有被Block,但被Pending但不會立即遞達,而是在合適的時候
    這裏寫圖片描述
    信號產生時,內核在進程控制塊中設置該信號的未決標誌,直到信號遞達才清除該標誌。
    SIGHUP信號未阻塞也未產生過,當它遞達時執行默認處理動作。
    SIGINT信號產生過,但正在被阻塞,所以暫時不能遞達。雖然它的處理動作是忽略,但在沒有解除阻塞之前不能忽略這個信號,因爲進程仍有機會改變處理動作之後再解除阻塞。SIGQUIT信號未產生過,一旦產生SIGQUIT信號將被阻塞,它的處理動作是用戶自定義函數sighandler
  • 信號的幾個缺點:
    1.系統開銷太大
    2.發送信號的進程需要進行系統調用
    3.數量非常有限
    4.不能傳送數據及參數
    首先介紹一個數據類型:
    sigset_t
    是位圖類型,用戶想要訪問block表和pending表需要通過系統調用接口
    信號集操作函數
頭文件:#include<signal.h>
int sigemptyset(sigset_t *set);設置爲空
int sigfillset(sigset_t *set);填充
int sigaddset(sigset_t *set,int signo);添加
int sigdelset(sigset_t *set,int signo);刪除
int sigismember(const sigset_t *set,int signo);判斷是否在信號集中

前四個函數都是成功返回0,出錯返回-1。
sigismember是一個布爾函數,用於判斷一個信號集的有效信號中是否包含某種信號,若包含則返回1,不包含則返回0,出錯返回-1。

  • 可以調用sigprocmask可以讀取或更改進程的信號屏蔽字(阻塞信號集)
    阻塞信號集也叫作當前進程的信號屏蔽字,這裏的屏蔽是阻塞而不是忽略
頭文件:#include<signal.h>
函數原型:int sigpromask(int how,const sigset_t *set,sigset_t *oset);
返回值:若成功返回0;若出錯則爲1
參數:sigset_t -> 信號集
      第三個參數爲輸出型參數,目的是爲了保存老的信號屏蔽字,若不想保存設置爲NULL,就可以

SIG_BLOCK
set包含了我們希望添加到當前信號屏蔽字的信號,相當於mask = mask|set
SIG_UNBLOCK
set包含了我們希望從當前信號屏蔽字中解除阻塞的信號,相當於mask = mask&set
SIG_SETMASK
設置當前信號屏蔽字爲set所指向的值,相當於mask = set
如果調用sigprocmask解除了對當前若干個未決信號的阻塞,則在sigprocmask返回前,至少將其中一個信號遞達。

  • 可以調用sigpending函數來獲取pengding信號集
頭文件:#include<signal.h>
函數原型:int sigpending(sigset_t *set);
返回值:若成功返回0;若出錯則爲1
功能:讀取當前進程的未決信號集,通過set參數傳出
  • 下面是用剛學的幾個函數做的實驗:
#include <stdio.h>
#include<signal.h>
#include<unistd.h>
void printsigset(sigset_t *set)
{
    int i = 0;
    for(;i<32;i++)
    {
        if(sigismember(set,i))//判斷指定信號是否在目標集和中
        {
            putchar('1');
        }
        else
        {
            putchar('0');
        }
    }
    puts("");
}
int main()
{
    sigset_t s,p;//定義信號集對象
    sigemptyset(&s);//清空,初始化
    sigaddset(&s,SIGINT);//相當於ctrl+c
    sigprocmask(SIG_BLOCK,&s,NULL);//設置阻塞信號集,阻塞SIGINT信號
    while(1)
    {
        sigpending(&p);//獲取未決信號集
        printsigset(&p);
        sleep(1);
    }
    return 0;
}

運行結果:

這裏寫圖片描述

程序運行時,每秒鐘把各個信號的未決狀態打印一遍,由於我們阻塞了SIGINT信號,按ctrl+c將會使SIGINT信號處於未決狀態,但按ctrl+*仍然可以終止程序,因爲*SIGQUIT信號沒有阻塞。

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