fork和exec中的信号继承探索
- 一、结论
- 二、代码验证
- 2.1 代码编写
- 2.2 代码执行
 
- 三、linux源码验证
- 四、APUE中的验证
- 五、其他
一、结论
- fork时子进程会继承父进程的信号处理方式,包括父进程设置信号为- SIG_DFL或- SIG_IGN或捕获后设置自定义处理函数。
- exce时子进程会继承父进程设置为- SIG_DFL或- SIG_IGN的信号。对于捕获后设置自定义处理函数的信号则不继承这个处理函数。
二、代码验证
2.1 代码编写
此处针对exec的情况进行验证,对于单纯fork后的情况不进行说明。
- 编写main.c。此处捕获SIGCHLD自定义handle处理方式。当main函数执行后,将fork子进程,同时子进程会exec加载son可执行程序
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>void handler(int sig) {int status;int pid = waitpid(-1, &status, WNOHANG);if (pid > 0) {printf(">>>>>>>>>>>>>>>A child process has exited, pid: %d\n", pid);}
}int main() {// 设置信号处理函数signal(SIGCHLD, handler);pid_t pid = fork();if (pid == -1) {// fork失败perror("fork");exit(EXIT_FAILURE);} else if (pid == 0) {// 子进程sleep(3);execlp("./son", "son", NULL);} else {// 父进程继续执行,不等待子进程while(1) {printf("Parent process, PID: %d, son PID: %d\n", getpid(), pid);sleep(3);}}return 0;
}
执行 gcc main.c -o main 进行编译
- 编写son.c,调用fork创建孙子,孙子进程创建后会exec加载grandson可执行程序
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>int main()
{pid_t pid = fork();if (pid == -1) {// fork失败perror("fork");exit(EXIT_FAILURE);} else if (pid == 0) {// 子进程sleep(3);execlp("./grandson", "grandson", NULL);} else {// 父进程继续执行,不等待子进程while(1) {printf("son process, PID: %d, grandson PID: %d\n", getpid(), pid);sleep(3);}}return 0;
}
执行 gcc son.c -o son 进行编译
- 编写grandson.c,代码中定期输出语句,类比执行业务代码。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>int main()
{while(1){printf("I am grandson, running!..., pid=%d\n", getpid());sleep(3);}return 0;
}
执行 gcc grandson.c -o grandson 进行编译
2.2 代码执行
输入 ./main 执行程序,首先会观察到有两个 ./main程序,业务此时fork了
 
 三秒后500721号进程会执行exec,替换为son程序。son程序加载后会fork一次,故此时有两个son进程
 
 三秒后500770号进程会执行exec,替换为grandson程序。此时有main,son和grandson进程存在
 
 当孙子进程退出时(执行kill -9杀死),它将会变为僵尸进程,因为它的父进程即son进程没有调用wait,waitpid或忽略SIGCHLD信号
 
 结论:验证了父进程捕获信号后自定义处理逻辑,是不继承到子进程的。
此外,当我们将main.c的signal(SIGCHLD, handler);替换为signal(SIGCHLD, SIG_IGN);后,按上述流程执行,杀死孙子进程时,现象如下:
 
 一旦执行杀死孙子进程,则ps检测不到了,证明它被真正杀死,而不会陷入僵尸状态。
结论:验证了父进程忽略某个信号后,会继承到子进程,子进程同样忽略此信号。
三、linux源码验证
此处从源码角度论证fork时子进程会继承父进程的信号处理方式,包括父进程设置信号为SIG_DFL或SIG_IGN或捕获后设置自定义处理函数。
 阅读fork.c源码可知:
 copy_process函数调用copy_sighand函数©_signal函数

- 信号处理函数的继承:在copy_process函数中,当创建新进程(线程)时,如果clone_flags中设置了CLONE_SIGHAND,则共享信号处理函数。如果没有设置,则会复制父进程的信号处理函数。这可以通过copy_sighand函数实现,该函数通过memcpy复制信号处理动作,即sig->action。
- 信号状态的复制:在copy_signal函数中,如果clone_flags中没有设置CLONE_THREAD,则会为新进程分配一个新的signal_struct结构体,并且复制父进程的信号限制和一些其他的信号状态。
四、APUE中的验证
- 本文的结论1为:fork时子进程会继承父进程的信号处理方式,包括父进程设置信号为SIG_DFL或SIG_IGN或捕获后设置自定义处理函数。APUE对结论1的阐述如下:

- 本文的结论2为:exce时子进程会继承父进程设置为SIG_DFL或SIG_IGN的信号。对于捕获后设置自定义处理函数的信号则不继承这个处理函数。APUE对结论2的阐述如下:

五、其他
上文探究了fork和exec对信号的继承情况,那么对于fd的继承情况如何呢?有待后续探索…
ref:
 https://www.cnblogs.com/yiyide266/p/13706799.html
 《UNIX环境高级编程》