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;//每次完成解除時,內層循環結束
}
}
}
}
結果如圖: