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
#include
#include
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
#include
#include
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
#include
#include
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;//每次完成解除时,内层循环结束
}
}
}
}
结果如图: