• Linux- 调用signal 设定特定信号sig的处理函数handler


    调用 signal 可以设定特定信号 sig 的处理函数 handler。进程收到另⼀个调用 kill 发送过来的信号 sig 时,便会开始执行 handler 函数。通过这⼀对函数便可以实现最基本的进程间通信,以下面的程序为例:

    #include 
    #include 
    #include 
    #include 
    #include 
    
    void sig_routine(int dunno) {
        switch (dunno)
        {
        case 6:
            printf("\tI am child process, I receive signal SIGABRT\n");
            break;
        }
    }
    
    int main() {
        pid_t pid;
        int status;
        int sig;
        signal(SIGABRT, sig_routine);
    
        if (!(pid = fork())) {
            printf("\tHi I am child process !\n");
            sleep(10);
            return 0;
        }
        else {
            sleep(1);
            kill(pid, SIGABRT); // // Sends a SIGABRT (abort program) signal
            wait(&status);
    
            if (WIFSIGNALED(status)) {
                printf("child process receive signal %d\n", WTERMSIG(status));
            }
            else {
                printf("child process exits normally with %d\n", WTERMSIG(status));
            }
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    运行结果如下:

    majn@tiger:~/C_Project/signal_project$ ./signal_demo 
            Hi I am child process !
            I am child process, I receive signal SIGABRT
    child process exits normally with 0
    
    • 1
    • 2
    • 3
    • 4

    这个程序展示了如何在父子进程之间使用信号。它创建一个子进程,父进程向子进程发送一个 SIGABRT 信号,子进程捕获并处理这个信号,然后继续执行并最终正常结束。

    程序的关键部分如下:

    1. 信号处理函数 sig_routine(int dunno):

      • 当子进程接收到 SIGABRT 信号(其值为6)时,它调用此函数来处理该信号。
      • 此函数仅打印消息,表示子进程已接收到该信号,并继续执行。
    2. 主程序:

      • 通过 signal(SIGABRT, sig_routine);,程序为 SIGABRT 信号设置了一个处理函数,即 sig_routine
      • 使用 fork() 创建子进程。
      • 子进程打印一条消息表示它是子进程,然后休眠10秒,并正常退出。
      • 父进程休眠1秒,确保子进程已经开始执行并注册了信号处理函数。然后,它向子进程发送一个 SIGABRT 信号。
      • 父进程使用 wait(&status) 等待子进程结束,并获取子进程的结束状态。
      • 根据子进程的结束状态,父进程打印相应的消息。

    输出结果如下:

    • 子进程首先输出 “Hi I am child process!”。
    • 当子进程接收到 SIGABRT 信号时,它输出 “I am child process, I receive signal SIGABRT”。
    • 最终,父进程输出 “child process exits normally with 0”,因为子进程正常结束,没有因为接收到信号而被终止。

    所以,这个程序的关键是理解信号的默认行为(例如,SIGABRT 默认会终止进程)可以被覆盖,如果为该信号注册了一个处理函数。在这个例子中,处理函数只是简单地打印了一条消息,而不是终止进程。


    如果不为该信号注册一个处理函数,则运行结果如下:

    #include 
    #include 
    #include 
    #include 
    #include 
    
    int main() {
        pid_t pid;
        int status;
        int sig;
        // signal(SIGABRT, sig_routine);
    
        if (!(pid = fork())) {
            printf("\tHi I am child process !\n");
            sleep(10);
            return 0;
        }
        else {
            sleep(1);
            kill(pid, SIGABRT); // // Sends a SIGABRT (abort program) signal
            wait(&status);
    
            if (WIFSIGNALED(status)) {
                printf("child process receive signal %d\n", WTERMSIG(status));
            }
            else {
                printf("child process exits normally with %d\n", WTERMSIG(status));
            }
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    运行结果为:

    majn@tiger:~/C_Project/signal_project$ ./signal_demo 
            Hi I am child process !
    child process receive signal 6
    
    • 1
    • 2
    • 3

    注意

    第一段代码中,父进程和子进程都为SIGABRT注册了处理函数sig_routine。这是因为,在fork之前,父进程已经调用了signal(SIGABRT, sig_routine);SIGABRT信号注册了处理函数。当fork被调用时,子进程继承了父进程的信号处理设置,所以子进程同样也为SIGABRT注册了sig_routine作为其处理函数。

    对于fork()之上的代码,子进程不会“重新执行”。但这并不意味着子进程不“知道”或不“具有”那部分代码的效果。实际上,子进程是父进程的一个近乎完整的副本,它继承了父进程在fork()之前的所有内存布局、变量的值、文件描述符、程序计数器、堆和栈的状态等。

    fork()被调用时,子进程从那个点开始执行,但它继承了父进程在该点之前的所有状态和上下文。

    例如,在第一段代码中:

    signal(SIGABRT, sig_routine);
    
    • 1

    尽管这行代码是在fork()之前执行的,但由于子进程继承了父进程的上下文和状态,子进程也会有相同的信号处理程序设置为SIGABRT

    所以,虽然子进程不会重新执行fork()之前的代码,但它确实继承了那些代码执行的结果和状态。

  • 相关阅读:
    运筹学基础【三】 之 决策
    Python爬虫(8)
    CS224W Colab_1 笔记
    Android绘图学习(一)
    微前端:quankun
    【Cherno的OpenGL视频】Batch rendering - An intro
    Python和Excel的完美结合:常用操作汇总(案例详析)
    git解决冲突和推送历史版本到指定分支上
    java-php-net-python-校园后勤计算机毕业设计程序
    Java核心篇,二十三种设计模式(十三),行为型——责任链模式
  • 原文地址:https://blog.csdn.net/weixin_43844521/article/details/133243246