Linux 信號詳解五(信號阻塞,信號未決)


信號在內核中的表示
執行信號的處理動作成為信號遞達(Delivery),信號從產生到遞達之間的狀態稱為信號未決(Pending)。進程可以選擇阻塞(Block)某個信號。
被阻塞的信號產生時將保持在未決狀態,直到進程解除對此信號的阻塞,才執行遞達的動作
注意:阻塞和忽略是不同的,只要信號被阻塞就不會遞達,而忽略是在遞達之后可選的一種處理動作
信號不會丟失,如果信號被阻塞,只會保持信號未決,但是信號不丟失
只要取消阻塞 信號依然會到來

 

①PCB進程控制塊中有信號屏蔽狀態字(block),信號未決狀態字(pending)還有是否忽略標識
②信號屏蔽狀態字(block):1代表阻塞,0代表不阻塞;信號未決狀態字(pending):1代表未決,0代表信號遞達
③向進程發送SIGINT,內核首先判斷信號屏蔽狀態字是否阻塞,如果信號屏蔽狀態字阻塞,信號未決狀態字(pengding)相應位置1;
若阻塞解除,信號未決狀態字(pending)相應位置0,表示信號可以遞達了。 ④block狀態字,pending狀態都是64bit,分別代表Linux系統中的64個信號。例如SIGINT是2號信號,對應block狀態字中的第二位 ⑤block狀態字用戶可以讀寫,pending狀態字用戶只能讀,這是新號的設計機制。

 

信號集操作函數(block狀態字表示)
#include <signal.h>
int sigemptyset(sigset_t *set);
sigset_t *set實質上是8個字節大小的變量(8個字節64bit,代表Linux的64個信號)
sigemptyset將狀態字置零
int sigfillset(sigset_t *set);
sigfillset將狀態字全部置1(32號信號和33號信號除外int sigaddset(sigset_t *set,int signo);
將信號signo加入到信號集set中
int sigdelset(sigset_t *set,int signo);
將信號signo從信號集中刪除
int sigismember(const sigset *set,int signo);
判斷signo是否在信號集中

 

sigprocmask讀取或者更改進程的信號屏蔽狀態字(block)
#include <signal.h>
int sigprocmask(int how,const sigset_t *set,sigset * oset);
成功返回0,出錯返回-1
如果oset是非空指針,則讀取進程的當前信號屏蔽狀態字通過oset參數傳出,如果set是非空指針,則更改進程的信號屏蔽狀態字,參數how只是如何更改。
如果oset和set都是非空指針,則先將原來的信號屏蔽字備份到oset里,然后根據set和how參數更改信號屏蔽字。 how含義
--SIG_BLOCK set包含了我們希望添加到當前信號屏蔽字的信號,相當於mask=mask|set(位或運算) --SIG_UNBLOCK set包含了我們希望從當前信號屏蔽字中解除阻塞的信號,相當於mask=mask^set(位異或運算) --SIG_SETMASK 設置當前信號屏蔽字為set所指向的值,相當於mask=set

 

int sigpending(sigset_t *set);
獲取信號未決狀態字(pending)信息 

 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>

void printsigset(const sigset_t *pset)
{
    int i = 0;
    //遍歷64個信號,
    for (; i < 64; i++)
    {
        //信號從1開始   判斷哪些信號在信號未決狀態字中
        if (sigismember(pset, i + 1))
            putchar('1');
        else
            putchar('0');
        //fflush(stdout);
    }
    printf("\n");
}

void catch_signal(int sign)
{
    switch (sign)
    {
    case SIGINT:
        printf("accept SIGINT!\n");
        exit(0);
        break;
    case SIGQUIT:
        printf("accept SIGQUIT!\n");
        //取消信號阻塞
        //定義信號集
        sigset_t uset;
        //清空信號集
        sigemptyset(&uset);
        //將SIGINT信號加入到信號集中
        sigaddset(&uset,SIGINT);
        //進行位異或操作,將信號集uset更新到進程控制塊PCB結構中,取消阻塞信號SIGINT
        sigprocmask(SIG_UNBLOCK,&uset,NULL);
        break;
    }
}

int main(int arg, char *args[])
{
    //定義未決信號集(pending)
    sigset_t pset;
    //定義阻塞信號集(block)
    sigset_t bset;
    //清空信號集
    sigemptyset(&bset);
    //將信號SIGINT加入到信號集中
    sigaddset(&bset, SIGINT);
    //注冊信號
    if (signal(SIGINT, catch_signal) == SIG_ERR)
    {
        perror("signal error");
        return -1;
    }
    if (signal(SIGQUIT, catch_signal) == SIG_ERR)
    {
        perror("signal error");
        return -1;
    }
    //進行位或操作,將信號集bset更新到進程控制塊PCB結構中,阻塞信號SIGINT(即使用戶按下ctrl+c,信號也不會遞達)
    sigprocmask(SIG_BLOCK, &bset, NULL);
    while (1)
    {
        /*
         * 獲取當前信號未決信息,即使在sigprocmask()函數中設置了信號阻塞,
         * 但是如果沒有信號的到來,信號未決狀態字對應位依然是0
         * 只要有信號到來,並且被阻塞了,信號未決狀態字對應位才會是1
         * */
        sigpending(&pset);
        //打印信號未決信息
        printsigset(&pset);
        sleep(2);
    }
    return 0;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM