Child 在没有处理程序的情况下处理 SIGQUIT?

Child handling SIGQUIT without handler?

我正在了解信号​​以及当进程被分叉时它们的处理程序会发生什么。我从 了解到“必须在 child 中安装信号”,如果在从 parent 发送信号之前没有发生这种情况,则不会调用处理程序。我想找出这意味着什么,所以使用 strace 看看会发生什么。这是我的代码。我不关心代码批评,除非我做错了什么导致我的问题:

#include <cassert>
#include <fcntl.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <ctype.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>

using namespace std;

void sighup(int signo) {
    signal(SIGHUP, sighup); /* reset signal */
    printf("CHILD: I have received a SIGHUP\n");
}

void sigint(int signo) {
    signal(SIGINT, sigint); /* reset signal */
    printf("CHILD: I have received a SIGINT\n");
}

void sigquit(int signo) {
    printf("CHILD: I have received a SIGQUIT\n");
    exit(0);
}


int main(void) {

    int pid;
    cout << "Current PID is " << getpid() << endl;
    signal(SIGHUP, sighup); /* set function calls */
    signal(SIGINT, sigint);
    signal(SIGQUIT, sigquit);

    cout << "Waiting to fork..." << endl;
    int x; cin >> x;

    /* get child process */
    if ((pid = fork()) < 0) {
        perror("fork");
        exit(1);
    }

    if (pid == 0) {

        /* child */
        for (;;); /* loop for ever */

    }
    else {

        signal(SIGHUP, SIG_DFL);
        signal(SIGINT, SIG_DFL);
        signal(SIGQUIT, SIG_DFL);

        /* parent */
        /* pid hold id of child */
   sleep(3);
        printf("\nPARENT: sending SIGHUP\n\n");
        kill(pid, SIGHUP);
   int err = errno;
     cout << "sent SIGHUP, err is " << err;
     
        sleep(3); /* pause for 3 secs */
        printf("\nPARENT: sending SIGINT\n\n");
        kill(pid, SIGINT);
   err = errno;
     cout << "sent SIGINT, err is " << err;
     
        sleep(3); /* pause for 3 secs */
        printf("\nPARENT: sending SIGQUIT\n\n");
   
        kill(pid, SIGQUIT);
      err = errno;
     cout << "sent SIGINT, err is " << err;
        sleep(3);
    }

    return 0;
}

代码暂停,同时用户在显示其 PID 后输入一个整数,以便我可以附加到它。假设parent PID为11256,child PID为11313,我输入'11'恢复parent;这是 strace 的输出:

ubuntu@ubuntu-xenial:~$ sudo strace -f -p 11256
strace: Process 11256 attached
read(0, "11\n", 1024)                   = 3
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7fad6078fa10) = 11313
rt_sigaction(SIGHUP, {SIG_DFL, [HUP], SA_RESTORER|SA_RESTART, 0x7fad5fe5f4c0}, {0x400bd7, [HUP], SA_RESTORER|SA_RESTART, 0x7fad5fe5f4c0}, 8) = 0
rt_sigaction(SIGINT, {SIG_DFL, [INT], SA_RESTORER|SA_RESTART, 0x7fad5fe5f4c0}, {0x400bfe, [INT], SA_RESTORER|SA_RESTART, 0x7fad5fe5f4c0}, 8) = 0
rt_sigaction(SIGQUIT, {SIG_DFL, [QUIT], SA_RESTORER|SA_RESTART, 0x7fad5fe5f4c0}, {0x400c25, [QUIT], SA_RESTORER|SA_RESTART, 0x7fad5fe5f4c0}, 8) = 0
nanosleep({3, 0}, strace: Process 11313 attached
0x7ffe3a8df8a0)       = 0
[pid 11256] write(1, "\nPARENT: sending SIGHUP\n", 24) = 24
[pid 11256] write(1, "\n", 1)           = 1
[pid 11256] kill(11313, SIGHUP)         = 0
[pid 11256] nanosleep({3, 0},  <unfinished ...>
[pid 11313] --- SIGHUP {si_signo=SIGHUP, si_code=SI_USER, si_pid=11256, si_uid=1000} ---
[pid 11313] rt_sigaction(SIGHUP, {0x400bd7, [HUP], SA_RESTORER|SA_RESTART, 0x7fad5fe5f4c0}, {0x400bd7, [HUP], SA_RESTORER|SA_RESTART, 0x7fad5fe5f4c0}, 8) = 0
[pid 11313] write(1, "CHILD: I have received a SIGHUP\n", 32) = 32
[pid 11313] rt_sigreturn({mask=[]})     = 0
[pid 11256] <... nanosleep resumed> 0x7ffe3a8df8a0) = 0
[pid 11256] write(1, "sent SIGHUP, err is 0\nPARENT: se"..., 45) = 45
[pid 11256] write(1, "\n", 1)           = 1
[pid 11256] kill(11313, SIGINT)         = 0
[pid 11256] nanosleep({3, 0},  <unfinished ...>
[pid 11313] --- SIGINT {si_signo=SIGINT, si_code=SI_USER, si_pid=11256, si_uid=1000} ---
[pid 11313] rt_sigaction(SIGINT, {0x400bfe, [INT], SA_RESTORER|SA_RESTART, 0x7fad5fe5f4c0}, {0x400bfe, [INT], SA_RESTORER|SA_RESTART, 0x7fad5fe5f4c0}, 8) = 0
[pid 11313] write(1, "CHILD: I have received a SIGINT\n", 32) = 32
[pid 11313] rt_sigreturn({mask=[]})     = 0
[pid 11256] <... nanosleep resumed> 0x7ffe3a8df8a0) = 0
[pid 11256] write(1, "sent SIGINT, err is 0\nPARENT: se"..., 46) = 46
[pid 11256] write(1, "\n", 1)           = 1
[pid 11256] kill(11313, SIGQUIT)        = 0
[pid 11256] nanosleep({3, 0},  <unfinished ...>
[pid 11313] --- SIGQUIT {si_signo=SIGQUIT, si_code=SI_USER, si_pid=11256, si_uid=1000} ---
[pid 11313] write(1, "CHILD: I have received a SIGQUIT"..., 33) = 33
[pid 11313] lseek(0, -1, SEEK_CUR)      = -1 ESPIPE (Illegal seek)
[pid 11313] exit_group(0)               = ?
[pid 11313] +++ exited with 0 +++
<... nanosleep resumed> {2, 996222312}) = ? ERESTART_RESTARTBLOCK (Interrupted by signal)
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=11313, si_uid=1000, si_status=0, si_utime=894, si_stime=1} ---
restart_syscall(<... resuming interrupted nanosleep ...>) = 0
write(1, "sent SIGINT, err is 0", 21)   = 21
lseek(0, -1, SEEK_CUR)                  = -1 ESPIPE (Illegal seek)

我们可以在 parent 中看到对 rt_sigaction 的调用以安装 SIGHUP、SIGINT 和 SIGQUIT 处理程序,然后在 child 中也可以看到对 SIGHUP 和 SIGINT 的调用。

问题 1) 这就是在 child 中“安装”处理程序的意思吗?我只是假设(可能是错误的)不会进行任何系统调用,并且 child 在分叉之后只是“准备就绪”,实际上 re-executing 代码首先在parent。我错了吗?

问题 2) 我看到 rt_sigaction 在 child 中被调用用于 SIGHUP 和 SIGINT,但不是 SIGQUIT。然而,这个处理程序仍在 child 中被调用(我们看到“CHILD:我收到了一个 SIGQUIT”被写入)。当 apparently 还没有安装处理程序时,child 仍然如何处理这个信号?

Is this what is meant by "installing" the handlers in the child? I just assumed (proabably erroneously) that no system calls would be made, and that the child would just be "ready to go" after the fork, without actually re-executing code that was first carried out in the parent.

通过调用 signalsigaction 安装信号处理程序;在 Linux 上,这两个函数都进行了 rt_sigaction 系统调用。

当进程分叉时,子进程简单地从其父进程继承所有信号设置。没有代码被重新执行。

I see rt_sigaction being called in the child for SIGHUP and SIGINT, but not SIGQUIT. Yet, the handler for this is still being called in the child (we see "CHILD: I have received a SIGQUIT" being written).

您的 sighupsigint 函数调用 signal,这会进行 rt_sigaction 系统调用以重新安装处理程序。但是,您的 sigquit 函数只执行 printf 而不会调用 signal.