• 系统编程-信号-总体概述和signal基本使用


     

    信号章节 -- 信号章节总体概要

     

    信号基本概念

    信号是异步事件,发送信号的线程可以继续向下执行而不阻塞。

     

    信号无优先级。

    1到31号信号是非实时信号,发送的信号可能会丢失,不支持信号排队。

    31号信号到64是实时信号, 发送的信号都会被接收, 支持信号排队。

     

    信号在Linux内核头文件中的宏定义

     

    信号的处理

    由于进程启动时,SIGUSR1和SIGUSR2被忽略,一般我们可以在有需要时,去捕获这两个信号,进而调用自己的处理函数。相应的,我们的程序其他地方去发送相应的信号。

     

    signal函数原型 以及使用时所要包含的头文件

    和下面的是等价的:

     

    实验1  signal基本使用

    实验1.1

    #include <stdio.h>
    #include <signal.h>
    #include <unistd.h>
    
    //定义信号处理函数
    //signo: 进程捕获到的信号
    void sig_handler(int signo){
        printf("%d, %d occured 
    ", getpid(), signo);
    }
    
    int main(){
    
        #if 1  // 屏蔽这块代码,就是不捕获这俩信号
        //向内核登记信号处理函数以及信号值  
        if(signal(SIGTSTP, sig_handler) == SIG_ERR){
    
            perror("signal error");
        }
        if(signal(SIGINT, sig_handler) == SIG_ERR){
    
            perror("signal sigint error");
        }
        #endif
    
        while(1){
            sleep(1);
            printf("- hello -
    ");
        };
    
        return 0;
    }

    编译运行

    同时,根据这里的打印也可以看出,

    SIGINT信号就是2号信号, 我们在键盘上按下CTRL+C就可以发送该信号了。

    SIGTSTP信号就是20号信号,我们在键盘上按下CTRL+Z就可以发送该信号了。20号信号的备注就是 Keyboard stop, 即通过键盘发信号让进程停止。

     

     

    实验1.2

    如果屏蔽实验1内捕获这俩信号的代码块,运行效果如下

    常用知识点补充:

    19) SIGSTOP 20) SIGTSTP

    19号信号和29号信号的相同点: 都可以使得进程暂停,并且收到SIGCONT信号后可以让进程重新运行。

    19号信号和29号信号的不同点:    SIGSTOP不可以捕获(即使用信号处理函数)。

    那么,我们来让刚才停止的a.out继续运行吧:

    先查看a.out的pid

    可见a.out的pid是8349

    我们通过kill来发SIGCONT信号(18号信号)让a.out继续运行

    可见,a.out又继续运行起来了,

    然而,需要注意的是,通过18号信号被继续执行的进程:当终端内按下CTRL+C,则不能使得该进程终止了;且按下CTRL+Z,终端内也毫无迹象;但是可以通过kill -9被杀死。

    根据实测,是这样的,事实胜于雄辩。这个问题的原因以及背后隐藏的暂时我们还不知的相应知识点,可以留待以后探索,我们先暂且知道这么一回事就行了。

     

    实验2  SIG_DFL 和 SIG_IGN 使用

    #include <stdio.h>
    #include <signal.h>
    #include <unistd.h>
    
    //定义信号处理函数
    //signo: 进程捕获到的信号
    void sig_handler(int signo){
        printf("%d, %d occured 
    ", getpid(), signo);
    }
    
    int main(){
        printf("pid: %d 
    ", getpid());
    
        #if 1  // 屏蔽这块代码,就是不捕获这俩信号
        //向内核登记信号处理函数以及信号值  
        if(signal(SIGTSTP, SIG_IGN) == SIG_ERR){
    
            perror("signal error");
        }
        if(signal(SIGINT, SIG_DFL) == SIG_ERR){
    
            perror("signal sigint error");
        }
        #endif
    
        while(1){
            sleep(1);
            printf("- hello -
    ");
        };
    
        return 0;
    }
    

    此时,按下CTRL+Z对程序运行将毫无影响,而CTRL+C则采用默认方式,即结束进程。

     

     

    实验3 SIGUSR1 和 SIGUSR2 使用

    注意,这两个信号在进程启动时默认是被忽略的。

    #include <stdio.h>
    #include <signal.h>
    #include <unistd.h>
    
    //定义信号处理函数
    //signo: 进程捕获到的信号
    void sig_handler(int signo){
        printf("%d, %d occured 
    ", getpid(), signo);
    }
    
    int main(){
        printf("pid: %d 
    ", getpid());
    
        #if 1  // 屏蔽这块代码,就是不捕获这俩信号
        //向内核登记信号处理函数以及信号值  
        if(signal(SIGUSR1, sig_handler) == SIG_ERR){
            perror("signal error");
        }
        if(signal(SIGUSR2, sig_handler) == SIG_ERR){
            perror("signal sigint error");
        }
        #endif
    
        while(1){
            sleep(1);
            printf("- hello -
    ");
        };
    
        return 0;
    }
    

    编译运行,同时在另一个终端内发送信号 kill -SIGUSR1 10452  、 kill -SIGUSR2 10452

     

     

    实验4

    知识点:SIGKILL 和 SIGSTOP不能被忽略,也不能被捕获。

    本实验将尝试捕获SIGKILL和SIGSTOP,并以SIG_IGN的方式进程处理。

    核心代码展示:

    编译运行将返回SIG_ERR,如下图所示

     

    实验5  SIGCHLD

    #include <stdio.h>
    #include <signal.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <sys/wait.h>
    
    //定义信号处理函数
    //signo: 进程捕获到的信号
    void sig_handler(int signo){
        printf("%d, %d occured 
    ", getpid(), signo);
        wait(NULL);
    }
    
    int main(void)
    {
        pid_t pid;
    
        if(signal(SIGCHLD, sig_handler) == SIG_ERR){
            perror("signal error");
        }
    
        pid = fork();
        if (pid < 0)
        {
            printf("fork error");
        }
        else if (pid == 0) /* first child : 子进程 */
        { 	
            sleep(2);
    	printf("pid: child  =%ld
    ", (long)getpid());
            exit(0);
        }
    
        // 使用信号的方式,父进程不必在此处阻塞调用wait,可以继续向下执行自己的任务。
        while(1){
            sleep(1);
            printf("father can does his own things 
    ");
        }
    }

    编译运行:

    实验中可见,父进程收到了子进程的17号信号,17号信号就是SIGCHLD信号(或写作SIGCLD)

    使用信号来回收子进程后,父进程不必在阻塞调用wait,可以继续向下执行自己的任务。这个例子充分体现出了信号是一个异步事件。

    在父进程还存活的期间,子进程退出将不会产生僵尸进程。

    PS:父进程死后,肯定不会有其子进程还仍然是僵尸进程,因为子进程们会在其父进程死后被1号进程领养,进而被1号进程回收掉所占用的系统资源。

     

    .

    /************* 社会的有色眼光是:博士生、研究生、本科生、车间工人; 重点大学高材生、普通院校、二流院校、野鸡大学; 年薪百万、五十万、五万; 这些都只是帽子,可以失败千百次,但我和社会都觉得,人只要成功一次,就能换一顶帽子,只是社会看不见你之前的失败的帽子。 当然,换帽子决不是最终目的,走好自己的路就行。 杭州.大话西游 *******/
  • 相关阅读:
    Windows之shortcut
    VHDL之code structure
    Embedded之Makefile
    VHDL_LIB之DFF
    VCS之Git
    潭州课堂25班:Ph201805201 django框架 第五课 自定义简单标签,包含标签,模型类创建,梳理类创建 (课堂笔记)
    潭州课堂25班:Ph201805201 django框架 第四课 模板常用标签,模板继承与引用,自定义过渡器 (课堂笔记)
    潭州课堂25班:Ph201805201 django框架 第三课 模板路径,变量,过滤器,静态文件的引用 (课堂笔记)
    潭州课堂25班:Ph201805201 django框架 第二课 url,,include,kwargs,name的使用 (课堂笔记)
    潭州课堂25班:Ph201805201 django框架 第一课 环境搭建 (课堂笔记)
  • 原文地址:https://www.cnblogs.com/happybirthdaytoyou/p/14595308.html
Copyright © 2020-2023  润新知