信号阻塞之信号屏蔽pending

本文介绍了信号的产生方式,如键盘输入、系统调用等,并详细解析了信号在内核中的表示,重点阐述了信号的阻塞、未决状态以及如何通过信号集操作函数进行管理。此外,还展示了如何使用sigprocmask函数改变信号屏蔽字,以及sigpending函数用于获取当前进程的未决信号集。通过示例代码展示了信号阻塞与解除的实践过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、(1)信号是怎么产生的?

  • 从键盘中产生(ctrl -c 、  ctrl -z 、 ctrl \)

  • 软硬件异常结合

  • 通过命令kill产生

  • 通过系统函数产生

  • 有软件产生
(2)信号的处理:
  • 忽略 
  • 默认动作 
  • 自定义捕捉 
     默认动作就是接收到信号知道怎么处理,比如指针异常实际就是操作系统检测到异常发给了进程11号信号,除0就是发给了进程8号信号,在进程收到这些信号就终止程序。
2、阻塞信号之信号屏蔽pending 
     上面我们知道了信号产生的各种原因,接下来这个内容是我要重点讲的。实际执行信号的处理动作称为信号递达(Delivery),信号从产生到递达之间的状态称为信号未决(Pending)。 进程可以选择阻塞(Block)某个信号。被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作。注意:阻塞和忽略是不同的,只要信号被阻塞将不能进行递达,除非解除阻塞。阻塞是在递达之前,忽略是在递达之后可选的一种处理动作。

(1)信号在内核中的示意图: 


其中block和pending都是位图,位图的位置都是信号的编号,block中内容1表示阻塞,0表示未阻塞,pending表中内容1表示收到信号,0表示未收到信号。handler表是函数指针数组。SIG_DFL是默认处理SIG_IGN是忽略,void signal是自定义捕捉。每个信号都有两个标志位分别表示阻塞和未决,还有一个函数指针表示处理动作。信号产生时。内核在进程控制块中设置该信号的未决标志,直到信号递达才清除该标志。所以操作系统发信号给进程就是操作系统修改了进程PCB中的pending表对应的bit位。

       未决和阻塞标志可以用相同的数据类型sigset_t来存储,sigset_t称为信号集,这个类型可以表示为每个信号的“有效”或“无效”状态,在阻塞信号集中“有效”和“无效”的含义是该信号是否被阻塞,而在未决信号集中“有效”和“无效”的含义是该信号是否处于未决状态。阻塞信号集也叫作当前进程的信号屏蔽字(Signal Mask),这里的“屏蔽”应该理解为阻塞而不是忽略。

(2)信号集操作函数:

int sigemptyset(sigset_t *set);//初始化set所指向的信号集,使其中所有信号的对应bit清零,表示该信号集不包含任何有效信号


 int sigfillset(sigset_t *set);//t初始化set所指向的信号集,使其中所有信号的对应bit置位有效,表示该信号集的有效信号包括系统支持的所有信号
 int sigaddset(sigset_t *set, int signo);//信号集中添加某种有效信号 
int sigdelset(sigset_t *set, int signo);//信号集中删除某种有效信号


 int sigismember(const sigset_t *set, int signo); //用于判断一个信号集的有效信号中是否包含某种信号

(3)sigprocmask

调用函数sigprocmask可以读取或更改进程的信号屏蔽字(阻塞信号集)。

 
 int sigprocmask(int how, const sigset_t *set, sigset_t *oset); 
返回值:若成功则为0,若出错则为-1。

①oset非空时,读取进程的当前信号屏蔽字通过pset参数传出;

②set非空时,更改进程的信号屏蔽字;

③两个指针都非空时,将原来的信号屏蔽字输出备份至oset,并根据how和set更改信号屏蔽字;

(4)how参数的含义:

1)SIG_BLOCK添加到当前信号屏蔽字的信号 
2)SIG_UNBLOCK从当前信号屏蔽字中解除的信号 
3)SIG_SETMASK设置当前信号屏蔽字为set所指向的值 

(5)sigpending

int sigpending(sigset_t *set);//sigpending读取当前进程的未决信号集,通过set参数传出。

调用成功则返回0,出错则返回-1。

1)信号屏蔽代码:

  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<unistd.h>
  4 #include<signal.h>
  5 #include<sys/types.h>
  6         
  7 void show_pending(sigset_t *p)                                        
  8 {       
  9     int i=1;
 10     for(;i<=31;i++)
 11     {   
 12     ┊   if(sigismember(p,i))
 13     ┊   {
 14     ┊   ┊   printf("1");
 15     ┊   }
 16     ┊   else 
 17     ┊   {
 18     ┊   ┊   printf("0");
 19     ┊   }
 20     }   
 21     printf("\n");    
 22 } 
 23 int main()
 24 { 
 25     sigset_t block_set,oblock_set;
 26     sigemptyset(&block_set);
 27     sigemptyset(&oblock_set);
 28   
 29     sigaddset(&block_set,2);
 30     sigprocmask(SIG_SETMASK,&block_set,&oblock_set);
 31   
 32     sigset_t pset;
 33     while(1)
 34     {
 35     ┊   sigpending(&pset);
 36     ┊   show_pending(&pset);
 37     ┊   sleep(1);
 38     }
 39     return 0;
 40 }                                                                     
运行结果:

(在没发2号信号pending表是全零,发了以后第二个位置变为1,其他都为0,2号信号被屏蔽,所以不能进行递达就有了第二个位置一直为1)



2)测试信号屏蔽与解除并递达:


运行结果:

图中就是进行屏蔽2号信号,并在count++到10后解除并递达。 


结果可以看出:由于我们阻塞了SIGINT信号,使用ctrl -c将会使SIGINT信号处于未决状态, 使用ctrl -z 或者ctrl -\可以终止程序,因为SIGQUIT信号没有阻塞。





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值