阻塞信號及信號屏蔽pending

1.信號在內核中的三種表示

(1)信號遞達:實際執行信號的處理動作;

(2)信號未決(pending):信號從產生到遞達的狀態,是一種記錄狀態;

(3)阻塞信號(block):被阻塞的信號不會遞達,它在產生時處於未決狀態,直到進程解除對這個信號的阻塞,纔會執行遞達,但不會立即遞達;

阻塞與忽略的不同:忽略是在信號遞達後的處理動作,而被阻塞的信號是不會遞達的;

2.信號在內核中的表示示意圖:

(1)每個PCB都包含三張表:block,pending,handler,block表和pending表有可以有相同的數據結構,是一張位圖,佔四個字節,pending表的0、1表示是否收到信號,block表的0、1表示是否被阻塞;

(2)每個信號都有阻塞標誌位block、未決標誌位pending和一個處理動作handler;

(3)信號產生時,內核在PCB中設置未決標誌位,當信號遞達時才清除該標誌位;
這裏寫圖片描述

上述圖中:
①1號信號未阻塞也未遞達;
②2號信號被阻塞,暫時不會遞達;
③3號信號未產生過,一旦產生將被阻塞;

3.信號集

未決標誌和阻塞標誌可以通過相同的數據類型sigset_t存儲,叫做信號集;

阻塞信號集也可以叫做信號屏蔽字;

信號集操作有很多函數,將在下面的信號屏蔽中進行介紹;

4.信號屏蔽
(1)sigprocmask函數: 可以讀取或更改進程的信號屏蔽字;

int sigprocmask(int how, const sigset_t *set , sigset_t *oset);
成功時返回0,出錯返回-1

①oset非空時,讀取進程的當前信號屏蔽字通過pset參數傳出;

②set非空時,更改進程的信號屏蔽字;

③兩個指針都非空時,將原來的信號屏蔽字輸出備份至oset,並根據how和set更改信號屏蔽字;

④how參數:
SIG_BLOCK—–set包含要添加的信號;
SIG_UNBLOCK—-set包含要解除阻塞的信號;
SIG_SETMASK—-設置當前信號屏蔽字爲set的值;

(2)pending函數:讀取當前進程的未決信號集

int pending(sigset_t *set);
成功時返回0,失敗時返回-1

(3)測試信號屏蔽與解除並遞達:
最終代碼3可以多次發送2號信號並解除,遞達
①代碼1:

#include<stdio.h>
#include<unistd.h>
#include<signal.h>

void showpending(sigset_t *pending)
{
    int i=1;
    for(;i<=31;i++)
    {
        if(sigismember(pending,i))//判斷指定信號是否在目標集合中
        {
            printf("1");
        }
        else
        {
            printf("0");
        }
    }
    printf("\n");
}

int main()
{
    sigset_t blockset,oblockset,pending;
    sigemptyset(&blockset);//初始化信號集
    sigemptyset(&oblockset);//初始化信號集
    sigaddset(&blockset,2);//在信號集中添加2號信號ctrl-c
    sigprocmask(SIG_SETMASK,&blockset,&oblockset);//設置當前的信號屏蔽字,並備份
    while(1)
    {
        sigpending(&pending);//獲取pending表
        showpending(&pending);
        sleep(1);
    }
}

結果如圖:

這裏寫圖片描述

程序每秒將31個信號的未決狀態打印一遍,按ctrl-c後第二個比特位變爲1,因爲2號信號被屏蔽,不能遞達,所以不被處理。
②代碼2:

#include<stdio.h>
#include<unistd.h>
#include<signal.h>

void handler(int sig)
{
    printf("get a sig:%d\n",sig);
}

void showpending(sigset_t *pending)
{
    int i=1;
    for(;i<=31;i++)
    {
        if(sigismember(pending,i))
        {
            printf("1");
        }
        else
        {
            printf("0");
        }
    }
    printf("\n");
}

int main()
{
    sigset_t blockset,oblockset,pending;
    sigemptyset(&blockset);
    sigemptyset(&oblockset);
    sigaddset(&blockset,2);
    signal(2,handler);//捕捉2號信號
    sigprocmask(SIG_SETMASK,&blockset,&oblockset);
    int count=0;
    while(1)
    {
        sigpending(&pending);
        showpending(&pending);
        sleep(1);
        if(count++==10)//當count加到10時,解除阻塞
        {
           printf("recover proc block set!\n");
           sigprocmask(SIG_SETMASK,&oblockset,NULL);
        }
    }
}

結果如圖:

這裏寫圖片描述

③代碼3:

#include<stdio.h>
#include<unistd.h>
#include<signal.h>

void handler(int sig)
{
    printf("get a sig:%d\n",sig);
}

void showpending(sigset_t *pending)
{
    int i=1;
    for(;i<=31;i++)
    {
        if(sigismember(pending,i))
        {
            printf("1");
        }
        else
        {
            printf("0");
        }
    }
    printf("\n");
}

int main()
{
    sigset_t blockset,oblockset,pending;
    sigemptyset(&blockset);
    sigemptyset(&oblockset);
    sigaddset(&blockset,2);
    while(1)//多加了一個循環,讓它可以多次屏蔽2號信號,並解除、遞達
    {
       signal(2,handler);
       sigprocmask(SIG_SETMASK,&blockset,&oblockset);
       int count=0;
       while(1)
       {
          sigpending(&pending);
          showpending(&pending);
          sleep(1);
          if(count++==10)
          {
             printf("recover proc block set\n");
             sigprocmask(SIG_SETMASK,&oblockset,NULL);
             break;//每次完成解除時,內層循環結束
          }
       }
    }
}

結果如圖:

這裏寫圖片描述

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