信号的内核表示

信号在内核中的三种状态:

阻塞、未决、递达(这里也可以用handler表示处理),如下图:

递达:执行信号处理动作称为信号的递达(delivery)

未决:从信号的产生到信号的递达之间的状态称为未决(peeding)

阻塞:进程可以阻塞(block)信号,这时信号处在未决状态,直到进程接触对信号的阻塞,才执行递达动作

修改block的值:

sigprocmask(int how,const  sigset_t *set,sigset_t *old);

how: 如何将信号集设置入内核

          SIG_BLOCK:在内核原本mask的基础上将set的mask加入

          SIG_UNBLOCK:在内核原本mask的基础上去掉set中的mask

          SIG_SETMASK:直接等于set,不考了原本内核中的值

sigset_t :block 对外提供的接口

int sigemptyset():清空信号集

int sigfillset():把信号集置1

int sigaddset():将指定某个比特位置为1,也就是将某一个信号加入信号集

int sigdelset():将指定某个比特位清零,也就时将这个信号量从信号集中删除

int sigismember():判断一个信号量在不在这个信号集中

将先前信号量集的值通过old返回

这几个函数都是在用户空间倒腾,只有sigprocmask()是映射到内核中的

未决状态的获取:

peeding状态只能获取不能设置!

获取内核的未决状态集:sigpending(sigset_t *set)

阻塞与未决的具体使用:

  1 #include<stdio.h>                                                           
  2 #include<unistd.h>
  3 #include<string.h>
  4 #include<stdlib.h>
  5 #include<signal.h>
  6 void handler(int s)
  7 {
  8     printf("%d recv\n",s);
  9 }
 10 void handler_quit(int s)
 11 {
 12     sigset_t set;
 13     sigemptyset(&set);
 14     sigaddset(&set,SIGINT);
 15     sigprocmask(SIG_UNBLOCK,&set,NULL);
 16 }
 17 int main()
 18 {
 19     signal(SIGINT,handler);
 20     signal(SIGQUIT,handler_quit);
 21     sigset_t set;
 22     sigset_t pset;
 23     sigemptyset(&set);
 24     sigaddset(&set,SIGINT);
 25     sigprocmask(SIG_BLOCK,&set,NULL);
 26     for(;;)
 27     {
 28         sleep(1);
 29         sigemptyset(&pset);
 30         sigpending(&pset);
 31         int i=1;
 32         for( i=1;i<=_NSIG;i++)
 33         {
 34             if(sigismember(&pset,i))
 35             {
 36                 printf("1");
 37             }
 38             else
 39             {
 40                 printf("0");
 41             }
 42         }
 43     }                                                                       
 44 }

信号忽略是信号递达后处理的一个动作,信号屏蔽是信号没有递达

信号处理函数中的设置,只在信号处理函数中有效

竞态 (pause):

int pause(void)

实现暂停功能,将当前进程挂起,转存储调度,直到有信号递达才能唤醒该进程

让操作系统调度起别的进程运行

用pause模拟实现sleep函数:

首先我们要知道:sleep是可中断的,返回值为距离设定设定时间还差多少秒,如果我们设定的是sleep(5),我们在2秒时发送一个信号,那么返回值就为3

使用alarm(0)是避免出现其他信号打断时alarm再次出现又将其打断。

这里为了避免由于时间片的切换导致信号无法正常唤醒pause,我们可以引入带信号屏蔽版的pause

int sigsuspend(const sigset_t *mask);

1.sigsuspend=pause+信号屏蔽

2.sigsuspend等信号处理函数执行完后返回

  1 #include<stdio.h>                                                           
  2 #include<unistd.h>
  3 #include<signal.h>
  4 void handler(int s)
  5 {
  6     printf("signal arrive");
  7 }
  8 void handler2(int s)
  9 {
 10     
 11 }
 12 int mysleep(int sec)
 13 {
 14     signal(SIGALRM,handler2);
 15     sigset_t set,old,zset;
 16     sigemptyset(&set);
 17     sigemptyset(&old);
 18     sigemptyset(&zset);
 19     sigaddset(&set,SIGALRM);
 20     sigprocmask(SIG_BLOCK,&set,&old);
 21     alarm(sec);
 22     sigsuspend(&zset);
 23     sigprocmask(SIG_UNBLOCK,&set,&old);
 24     int n=alarm(0);
 25     return n;
 26 }
 27 int main()
 28 {
 29     signal(SIGINT,handler);
 30     for(;;)
 31     {
 32         printf(".");
 33         fflush(stdout);
 34         int n=mysleep(3);
 35         printf("%d\n",n);                                                   
 36     }
 37 
 38 }

注册信号升级版:

int sigaction(int signo,const struct sigaction*set,struct sigaction *old);

signo:捕获的信号

set:设置信号处理函数

返回值为old

struct sigaction{

void (*sa_handler)(int);//信号的处理函数

sigset_t  sa_mask;//在信号处理函数执行期间的信号屏蔽,是一个集合,使用时要用sigemptyset清空

int sa_flags;   //永远填0

在本信号执行期间会屏蔽掉再一次到来的本信号,别的信号不受影响,若要屏蔽其他信号可以加载mask中。

具体用例:

  1 #include<stdio.h>
  2 #include<signal.h>
  3 #include<unistd.h>
  4 void handler(int s)
  5 {
  6     printf("recv %d\n",s);
  7 }
  8 int main()
  9 {
 10     struct sigaction act;
 11     act.sa_handler=handler;
 12     sigemptyset(&act.sa_mask);
 13     act.sa_flags=0;                                                         
 14     sigaction(SIGINT,&act,NULL);
 15     for(;;)
 16     {
 17         pause();
 18     }
 19 }

猜你喜欢

转载自blog.csdn.net/enjoymyselflzz/article/details/81865990