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环境高级编程》