linux訊號的阻塞和未決

readyao發表於2015-12-17

執行訊號的處理動作稱為訊號遞達(Delivery),訊號從產生到遞達之間的狀態,稱為訊號未決(Pending)。

程式可以選擇阻塞(Block)某個訊號。被阻塞的訊號產生時將保持在未決狀態,直到程式解除對此訊號的阻塞,才執行遞達的動作。注意,阻塞和忽略是不同,只要訊號被阻塞就不會遞達,而忽略是在遞達之後可選的一種處理動作。


訊號在核心中的表示可以看作是這樣的:


1PCB程式控制塊中函式有訊號遮蔽狀態字(block)訊號未決狀態字(pending)還有是否忽略標誌(或是訊號處理函式)block狀態字、pending狀態字 64bit

2訊號遮蔽狀態字(block),1代表阻塞、0代表不阻塞;訊號未決狀態字(pending)的1代表未決,0代表訊號可以抵達了;它們都是每一個bit代表一個訊號,比如,bit0代表訊號SIGHUP;

3:比如向程式傳送SIGINT,核心首先判斷訊號遮蔽狀態字是否阻塞,如果該訊號被設為為了阻塞的,那麼訊號未決狀態字(pending)相應位製成1;若該訊號阻塞解除,訊號未決狀態字(pending)相應位製成0;表示訊號此時可以抵達了,也就是可以接收該訊號了。

 4遮蔽狀態字使用者可以讀寫,未決狀態字使用者只能讀;這是訊號設計機制。

API函式:

訊號集操作函式,對狀態字進行操作(遮蔽狀態字和未決狀態字):

#include <signal.h>
int sigemptyset(sigset_t *set);//將訊號集清空,共64bits
int sigfillset(sigset_t *set);//將訊號集置1
int sigaddset(sigset_t *set, int signum);//將signum對應的位置為1
int sigdelset(sigset_t *set, int signum);//將signum對應的位置為0
int sigismember(const sigset_t *set, int signum);//判斷signum是否在該訊號集合中,如果集合中該位為1,則返回1,表示位於在集合中


還有一個函式可以讀取更改遮蔽狀態字的API函式

#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
引數how有下面三種取值:
SIG_BLOCK:  將引數set指向的訊號集中設定的訊號新增到現在的遮蔽狀態字中,設定為阻塞;
SIG_UNBLOCK:將引數set指向的訊號集中設定的訊號新增到現在的遮蔽狀態字中,設定為非阻塞, 也就是解除阻塞;
SIG_SETMASK:將引數set指向的訊號集直接覆蓋現在的遮蔽狀態字的值;
如果oset是非空指標,則讀取程式的當前訊號遮蔽字通過oset引數傳出。
若成功則為0,若出錯則為-1

還有一個函式可以讀取未決狀態字(pending)資訊 

#include <signal.h>
int sigpending(sigset_t *set);

例項:

SIGINT訊號設定阻塞,檢視未決關鍵字 
傳送SIGINT訊號,檢視未決關鍵字 
傳送SIGQUIT訊號解除SIGINT訊號阻塞,檢視未決關鍵字 

剛開始設定SIGINT訊號為阻塞訊號,當按下Ctrl+c傳送中斷訊號SIGINT(值為2,所以後來第二位被置1)之前,未決狀態字的所有位都是0,因為此時沒有未抵達的訊號;
當傳送SIGINT訊號後,因為該訊號是阻塞的,所以未決狀態字將第二位置為了1,表示該訊號在這裡阻塞了;當我按下Ctrl+\傳送SIGQUIT訊號後,又將SIGINT訊號設定為了非阻塞的;此時可以看到未決狀態字的所有位都變為了0;並且也收到了剛才阻塞的SIGINT訊號;

/*************************************************************************
	> File Name: block_pending.cpp
	> Author: 
	> Mail: 
	> Created Time: 2015年12月17日 星期四 17時13分45秒
 ************************************************************************/

#include <iostream>
#include <signal.h>
#include <cstdlib>
#include <unistd.h>
using namespace std;


void handler(int num)
{
    if(num == SIGINT){
        cout << "剛才收到了訊號SIGQUIT, 取消了阻塞,收到中斷訊號.." << endl;
    }
    else if (num == SIGQUIT){
        //將SIGINT訊號設定為非阻塞的
        sigset_t un_bset;
        sigemptyset(&un_bset);
        sigaddset(&un_bset, SIGINT);
        sigprocmask(SIG_UNBLOCK, &un_bset, NULL);
    }
}

void print_pending(sigset_t * pset)
{
    int i = 0;
    cout << "未決狀態字(64位):";
    for (i = 1; i <= 64; ++i){
        if(sigismember(pset, i))
            cout << 1;
        else
            cout << 0;
        if(i % 8 == 0){
            cout << " ";
        }
    }
    cout << endl;
}
int main()
{
    sigset_t bset;
    sigset_t pset;
    
    //設定SIGINT訊號
    sigemptyset(&bset);
    sigaddset(&bset, SIGINT);

    signal(SIGINT, handler);
    signal(SIGQUIT, handler);
    //將SIGINT訊號設定為阻塞的
    sigprocmask(SIG_BLOCK, &bset, NULL);

    while(1){
        //得到未決狀態字
        sigpending(&pset);
        //顯示未決狀態字
        print_pending(&pset);

        sleep(1);
    }


    exit(0);
}


相關文章