• 七、进程间通信信号


    1、信号

    (1)概述

      信号是软件中断,进程接收信号后做出相应响应,它提供了一种处理异步事件的方法。每个信号都有名字,这些名字以SIG开头,信号都定义在<signal.h>头文件中,并且都是正整数常量。

     (2)怎么产生信号

    • 硬件

      • 执行非法指令
      • 访问非法内存
      • 驱动程序
    • 软件

      • Ctrl + C:中断信号。

      • Ctrl + | :退出信号。

      • Ctrl + Z:停止信号。

      • kil命令:程序调用kill()函数。

    (3)信号的处理方式

    • 忽略:进程当信号从来没有发生过。

    • 捕获:进程会调用相应的处理函数,进行相应的处理。

    • 默认:使用系统默认处理方式处理信号。

    (4)常用信号分析

    信号名 信号编号 产生原因 默认处理方式
    SIGHUP 1 关闭中断 终止
    SIGINT 2 ctrl+c 终止
    SIGQUIT 3 ctrl+\ 终止+转储
    SIGABRT 6 abort() 终止+转储
    SIGPE 8 算术错误 终止  
    SIGKILL 9 kill -9 pid 终止,不可捕获/忽略
    SIGUSR1 10 自定义   忽略  
    SIGSEGV 11 段错误 终止+转储  
    SIGUSR2 12 自定义 忽略
    SIGALRM 14 alarm() 终止
    SIGTERM 15 kill pid 终止
    SIGCHLD 17 (子)状态变化 忽略
    SIGSTOP 19  ctrl+z   暂停,不可捕获/忽略

     

     (5)设置信号处理方式

    #include<signal.h>
    
    typedef void (*sighandler_t)(int);
    
    sighandler_t signal(int signum,sighandler_t handler);
    
    •  参数
      • signum: 要设置的信号

      • handler:SIG_IGN:忽略;SIG_DFL:默认

      • void (*sighandler_t)(int):自定义

    • 返回值:
      • 成功:返回上一次设置的handler
      • 失败:SIG_ERR.
    • 实例:CTRL+C触发SIGINT信号,处理信号回调函数

    #include <stdio.h>
    #include <signal.h>
    #include <stdlib.h>
    void signal_handler(int sig)
    {
        printf("\nthis signa number is %d\n",sig);
    
        if(sig == SIGINT)
        {
            printf("I have get SIGINT!\n\n");
            printf("The signal has been restored to the default processing mode!\n\n");
            /*恢复信号为默认情况*/
            signal(SIGINT,SIG_DFL);
        }
    }
    
    int main(void)
    {
        printf("\nthis is an alarm test function\n\n");
        /*设置信号处理的回调函数*/
        signal(SIGINT,signal_handler);
        while(1)
        {
            printf("waiting for the SIGINT signal,please enter\"Ctrl+C\"...\n");
            sleep(1);
        }
        exit(0);
        return 0;
    }

    执行结果:

    (6)kill函数:给其他进程发送特定的信号

    #include <sys/types.h>
    #include <signal.h>
    
    int kill(pid_t pid,int sig);
    • 参数
      • pid:进程id.
      • sig:要发送的信号。
    • 返回值: 
      • 成功:0
      • 失败:-1 

    (7)raise函数:把信号发给进程自身

    #include <signal>
    
    int raise(int sig);
    • 参数
      • sig:要发送的信号。
    • 返回值: 
      • 成功:0
      • 失败:非0 

     (8)kiill和raise使用实例

     子进程使用rasie给自身进程发送SIGSTOP暂停信号,然后父进程通过kill发送SIGKILL给子进程杀死子进程。

    #include <stdio.h>
    #include <signal.h>
    #include <stdlib.h>
    #include <sys/types.h>
    
    int main(void)
    {
        pid_t pid;
        int ret;
        pid = fork();
        if(pid<0)
        {
            printf("create process error\n");
            exit(1);
        }
        else if(pid == 0)
        {
            printf("child is waiting for SIGSTOP signal!!\n\n");
    
            /*子进程停在这里*/
            raise(SIGSTOP);  //给子进程本身发暂停信号,阻塞到这。
    
            /*子进程没有机会运行到这里*/
            printf("child won't run here forever");
            exit(0);
    
        }
        else
        {
            /*睡眠3s,让子进程先执行*/
            sleep(3);
    
            /*发送SIGKILL信号杀死子进程*/
            if((ret=kill(pid,SIGKILL))==0)  //kill给其他进程发信号
            {
                printf("Parent kill %d!\n\n",pid);
            }
            /*等待子进程退出*/
            wait(NULL);
    
            /*父进程退出运行*/
            printf("parent exit!\n");
            exit(0);
    
        }
    
        return 0;
    }
    

    2、信号集处理函数

      内核通过读取未决信号集来判断信号是否应被处理。信号屏蔽字mask可以影响未决信号集。而我们可以在应用程序中自定义set来改变mask。已达到屏蔽指定信号的目的。综上:自定义信号集set(也为一个字,64位)通过信号集操作函数来改变信号屏蔽字mask,然后mask进一步来影响未决信号集,从而控制信号是否应该被屏蔽。后面我们只是研究常规信号,即1~31号!

    (1)屏蔽信号集

    • 手动
    • 自动

    (2)未处理信号集

      信号如果被屏蔽,则记录在未处理信号集中。

    • 非实时信号(1~31):不排队,只留一个。
    • 实时信号(34~64):排队,保留全部。

    (3)信号集相关

    sigset_t set; // typedef unsigned long sigset_t; 声明一个信号集变量set,信号集类型为sigset_t,为unsigned long(无符号长整型),64位。
    
    int sigemptyset(sigset_t *set);           //将set所指信号集清0 成功:0;失败:-1
    
    int sigfillset(sigset_t *set);           //将set所指信号集置1 成功:0;失败:-1
    
    int sigaddset(sigset_t *set, int signum);     //将某个信号加入信号集(即将其置1) 成功:0;失败:-1
    
    int sigdelset(sigset_t *set, int signum);     //将某个信号清出信号集(即将其置0) 成功:0;失败:-1
    
    int sigismember(const sigset_t *set, int signum); 判断某个信号是否在信号集中(是否为1)返回值:在集合:1;不在:0;出错:-1
    
    sigset_t类型的本质是位图。但不应该直接使用位操作,而应该使用上述函数,保证跨系统操作有效。
     
    使用设置好的信号集去修改信号屏蔽集:
     
    int sigprocmask(int how,const sigset_t *set,sigset_t *oldset);
    
    • how:
      • SIG_BLOCK:屏蔽某个信号(屏蔽集 | set)
      • SIG_UNBLOCK:打开某个信号(屏蔽集 & (~set))
      • SIG_SETMASK:屏蔽集 = set
    • oldset:保存就的屏蔽集的值,NULL表示不保存

     (4)实例:

    #include <stdio.h>
    #include <signal.h>
    #include <stdlib.h>
    void signal_handler(int sig)
    {
        printf("hello\n");
        sleep(5);
        printf("world\n");
    }
    
    int main(void)
    {
        /*设置信号处理的回调函数*/
        signal(SIGINT,signal_handler);
        while(1);
        return 0;
    }
    

     执行结果:

     

     由上面的结果,可知我们发送了一个SIGINT信号后,在回调函数里面打印helloworld,我们按Ctrl+C后执行hello,在这期间,多次按ctrl+C发送SIGINT信号,signal并没有每个信号都进行处理,如果他第一个信号没有处理完,就不会再响应其他信号。

    由于SIGINT信号编号为2,是一种非实时信号,所以只保留一个信号响应。

    我们发送编号为35的信号,此信号为实时信号,系统排队响应。

    #include <stdio.h>
    #include <signal.h>
    #include <stdlib.h>
    void signal_handler(int sig)
    {
        printf("hello\n");
        sleep(5);
        printf("world\n");
    }
    
    int main(void)
    {
        /*设置信号处理的回调函数*/
        signal(35,signal_handler);
        while(1);
        return 0;
    }

     

    在一个终端执行程序,在另一个终端通过kill发送编号为35的信号给signalsets的进程,然后就打印出了hello world.

    由于编号为35的信号是实时信号,排队处理,所以每个信号都被排队响应

     

     如果我们想实时响应非实时信号,可将信号集的这一信号置为1,如SIGINT信号,修改代码如下:

    #include <stdio.h>
    #include <signal.h>
    #include <stdlib.h>
    void signal_handler(int sig)
    {
        sigset_t set;
        
        sigemptyset(&set);  //信号集清0
        
        sigaddset(&set,SIGINT);   //将SIGINT加入信号集
    
        sigprocmask(SIG_UNBLOCK,&set,NULL);  //使用新设置的信号集去修改信号屏蔽集
        printf("hello\n");
        sleep(5);
        printf("world\n");
    }
    
    int main(void)
    {
        /*设置信号处理的回调函数*/
        signal(SIGINT,signal_handler);
        while(1);
        return 0;
    }

    执行结果如下:每个SIGINT都会被响应

     

     
  • 相关阅读:
    [转]三维成像原理
    loader如果你提前设width或height,loadComplete后显示不出来
    Flash调用Alchemy编译的代码时出现Error #1506的解决
    通过 IP 区分不同国家的用户
    Linux的进程组和会话
    Linux下安装 JDK(转备忘)
    程序中,调用Bison和Flex结合的小例子(语法分析中处理数据)
    从自己的程序中使用lex的一个小例子
    yum 删除软件要注意一点
    Linux下top命令
  • 原文地址:https://www.cnblogs.com/yuanqiangfei/p/16197559.html
Copyright © 2020-2023  润新知