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.
通过调用 signal
或 sigaction
安装信号处理程序;在 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).
您的 sighup
和 sigint
函数调用 signal
,这会进行 rt_sigaction
系统调用以重新安装处理程序。但是,您的 sigquit
函数只执行 printf
而不会调用 signal
.
我正在了解信号以及当进程被分叉时它们的处理程序会发生什么。我从
#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.
通过调用 signal
或 sigaction
安装信号处理程序;在 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).
您的 sighup
和 sigint
函数调用 signal
,这会进行 rt_sigaction
系统调用以重新安装处理程序。但是,您的 sigquit
函数只执行 printf
而不会调用 signal
.