进程间通信——信号、信号的操作、屏蔽信号,捕捉信号详解

 信号

信号是系统预先编译好的某些特定的事件,信号可以被产生,可以被接收,产生和接收的主体是进程。

信号的特点:1.简单 2.携带的信息量少 3.使用在某些特定的场景中

信号的状态

   1.产生状态       键盘:ctrl + c   命令:kill   系统函数 :kill()     软条件:定时器

   2.未决状态      等待被处理,没有处理之前就是未决状态

   3.递达状态

信号递达状态的三种响应方式

忽略:SIG_ING

默认:SIG_DFL

fun:自己写的信号处理函数

信号的处理优先级:信号的优先级比较高,当进程收到一个信号时,会先中断现在正在处理的事情优先处理信号,信号处理完后在继续刚才中断的工作

查看信号的种类:kill -l   或者 man 7 signal

信号相关的函数

1.kill 给某个进程发送信号 

           例如:int kill(pid_t pid,int sig)   pid 值给哪个进程发送信号,sig是发送的具体信号

           pid > 0 指定将信号发送给那个进程

            pid == 0 信号被发送到和当前进程在同一个进程组的进程

            pid == -1 将信号发送给系统上有权限发送的所有的进程

 7 int main()
  8 {
  9     pid_t pid = fork();
 10 
 11     if(pid > 0)
 12     {
 13         while(1)
 14         {
 15             printf("parent process = %d\n",getpid());
 16             sleep(1);
 17         }
 18 
 19     }
 20     else if(pid == 0)
 21     {
 22 
 23         sleep(2);
 24         kill(getppid(),SIGKILL);  //getppid()  获取父进程pid
 25     }
 26     return 0;
 27 }

父进程每一秒打印一个parent process,子进程在两秒之后向父进程发送kill函数,终止父进程

2.raise 给自己发送信号

          用kill封装一个raise函数:kill(getpid(),int sig);
          函数原型:int raise(int sig)

  8 int main()
  9 {
 10     pid_t pid = fork();
 11     if(pid > 0)
 12     {
 13         printf("parent process pid = %d\n",getpid());   // 打印父进程自己的pid  getpid()是用来获取当前进程的pid
 14         int s;
 15         pid_t wpid = wait(&s);   //用来监测子进程被杀死的返回值
 16 
 17         printf("child died pid = %d\n",wpid);
 18         if(WIFSIGNALED(s))   //监测是不是被信号终止
 19         {
 20             printf("died by signal = %d\n", WTERMSIG(s));   //如果是被信号终止,则打印信号的值
 21         }
 22 
 23     }
 24     else if(pid == 0)
 25     {
 26         raise(SIGQUIT);   //自己给自己发送3号函数 ctrl + \ 来终止程序
 27     }
 28     return 0;
 29 }

该父进程是被子进程的SIGQUIT(3号信号)杀死的,所以打印的是3

3.abort 异常终止信号  
           异常终止,打印出来的信号一定是6

  8 int main()
  9 {
 10     pid_t pid = fork();
 11     if(pid > 0)
 12     {
 13         printf("parent process pid = %d\n",getpid());  //打印父进程pid
 14         int s;
 15         pid_t wpid = wait(&s);
 16 
 17         printf("child died pid = %d\n",wpid);
 18         if(WIFSIGNALED(s))
 19         {
 20             printf("died by signal = %d\n", WTERMSIG(s)); //被异常终止信号终止的,所以打印6
 21         }
 22 
 23     }
 24     else if(pid == 0)
 25     {
 26         abort();  //异常终止信号(6号信号),终止子进程
 27     }
 28     return 0;
 29 }                                                             

4.alarm 设定定时器(每个进程只有一个定时器)
           使用的是自然定时法,不受进程状态影响
            函数原型:unsigned int alarm(unsigned int seconds);
           参数:秒
            当事件到达一个信号之后,函数发出一个信号:SIGALRM

7 int main()
  8 {
  9     alarm(1);   //定时器设定一秒
 10     int i = 0;
 11     while(1)
 12     {
 13         printf("%d\n",i++);  //在这一秒内打印i的值,可以看出计算机一秒内打印的数值的多少
 14     }
 15     return 0;
 16 }

在这里存在一个问题,就是如果讲打印的i的值重定向到文件中,则打印的值会比打印到终端的值多很多
realtime = 用户time+内核time+损耗time
损耗来自文件IO操作,IO操作会消耗大量的时间,所以数的少

5.概念:阻塞信号集,未决信号集
            1.阻塞信号集:要屏蔽的信号
                要阻塞某个信号,必须先设置阻塞信号集
            2.未决信号集:没有被处理的信号的集合
                信号产生,处于未决状态,进程收到信号之后,信号被放入未决信号集。    放入未决信号集中的信号等待处理,在处理之前需要做一件事情,判断阻塞信号集中的标志位是否是1,如果是1,不处理,如果是0,则处理

6.自定义信号集
            int sigemptyset(sigset_t *set) 将set集合置空
            int sigfillset(sigset_t *set) 将所有信号加入set集合
            int sigaddset(sigset_t *set,intsigno) 将signo信号加入到set集合
            int sigdelset(sigset_t *set,int signo) 从set集合中移除signo信号
            int sigismember(const sigset_t *set,int signo) 判断信号是否存在
7.sigprocmask函数
            屏蔽and接触信号屏蔽,将自定义信号集设置给阻塞信号集
            函数原型:int sigprocmask(int how,const sigset_t *set,sigset_t *oldset)
8.sigpending 读取当前进程的未决信号集
            函数原型:int sigpending(sigset_t *set)
            参数:set 内核将未决信号集写入set

  8 int main()
  9 {
 10     //创建自定义信号集合
 11     sigset_t myset;
 12     //将set集合致空
 13     sigemptyset(&myset);
 14     //将所有信号加入set集合
 15     sigfillset(&myset);
 16     //给自定义集合设置需要屏蔽的信号
 17     sigaddset(&myset,SIGINT);  //把2信号添加到自定义集合中
 18     sigaddset(&myset,SIGQUIT); //把3信号添加到自定义集合中
 19     sigaddset(&myset,SIGKILL); //把9信号添加到自定义集合中
 20     //将自定义的信号集设置给内核的阻塞信号集
 21     sigprocmask(SIG_BLOCK,&myset,NULL);
 22 
 23     while(1)
 24     {
 25         //读取当前信号的未决信号集
 26         sigset_t pendset;
 27         sigpending(&pendset);
 28 
 29        //循环监听1-32号信号,看是否存在,存在打印1,不存在打印0
 30         int i = 1;
 31         for(;i < 32;i++)
 32         {
 33             if(sigismember(&pendset,i))  //如果有该信号,就打印1,否则打印0
 34             {
 35                 printf("1");
 36             }
 37             else
 38             {
 39                 printf("0");
 40             }
 41 
 42         }
 43 
 44             printf("\n");
 45             sleep(1);
 46     }
 47     return 0;
 48 }

这里存在一个问题,就是打印的时候9好信号一直不会被监测到,这就牵扯到了SIGKILL的特性,不能被忽略,不能被阻塞,也不能被捕捉
the signals SIGKILL and SIGSTOP cannot be caught,blocked,ignored
 


9.信号的捕捉
            1.signal函数
                函数原型:typedef void(*sighandler_t)(int)     函数指针;回调函数
                        sighandler_t signal(int signum,sighandler_t handler)
                

  7 //设置回调函数
  8 void function(int num)
  9 {
 10     printf("catch signal = %d\n",num);  //捕捉到哪个信号就会打印哪个信号的值,被捕捉到之后也就不能发生作用了,所以在这里按ctrl+c不会终止进程,除非按ctrl+\或者kill
 11 }
 12 
 13 int main()
 14 {
 15    // 捕捉信号
 16    //注册捕捉函数
 17    signal(SIGINT,function);  //监测到SIGINT该信号就会调用回调函数function
 18 
 19    while(1)
 20    {
 21        printf("hello, I'm here!\n");
 22        sleep(2);
 23     }
 24     return 0;
 25 }


            2.sigaction   临时处理信号
                函数原型:int sigaction(int signum,const struct sigaction *act,struct sigaction * oldact)
                第二个参数
                struct sigaction{
                void (*sa_handler)(int);
                void (*sa_sigaction)(int,siginfo_t*,void*);
                sigset_t sa_mask;  //在信号处理函数执行过程中,临时处理指定的信号
                int sa_flags;  //0 -> 调用sa_handler sa_flagde值赋值0  
                };

  7 void myfun(int num)
  8 {
  9     printf("catch signal = %d\n",num);
 10     sleep(4); //在函数睡眠的时候,会忽略你键入的信号,等到睡醒之后才会执行你键入的操作
 11     printf("game over!\n");
 12 }
 13 
 14 int main()
 15 {
 16     //设置struct sigaction *act结构体
 17     struct sigaction act;
 18     act.sa_flags = 0;
 19 
 20     //先清空信号的集合act.sa_mask,然后添加需要捕捉的信号
 21     sigemptyset(&act.sa_mask);
 22     sigaddset(&act.sa_mask,SIGINT);
 23     act.sa_handler = myfun;
 24     sigaction(SIGQUIT,&act,NULL);
 25 
 26     while(1);
 27     return 0;
 28
 29 }


 

 

 

 

 

 

 

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值