Linux中子进程的信号处理与父进程的联系有以下三条:
- fork后子进程会继承父进程绑定的信号处理函数(很好解释,子进程会拷贝父进程的代码,包括信号处理函数)
- 如果子进程调用exec族函数,子进程代码段被新的程序覆盖,因此在这种情况下子进程不会继承信号处理函数
- 无论子进程是否调用exec族函数,都会继承父进程的信号掩码
下面通过程序逐个验证上面的三种说法:
结论一:子进程不调用exec族函数,则继承父进程的信号处理函数
/**
****************************************************************************************
* @file 30_子进程信号处理机制1.c
* @author GuiStar-李什么恩
* @version V1.1.0
* @date 2023-4-22
* @brief 子进程不调用exec族函数,则继承父进程的信号处理函数
****************************************************************************************
*/
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
static void sig_handler(int sig)
{
printf("Received signal:%d,PID=%d\n",sig,getpid());
}
int main(void)
{
struct sigaction sig={
0
};
int ret;
sig.sa_handler = sig_handler;
sig.sa_flags = 0;
ret = sigaction(SIGINT, &sig, NULL);
if(ret==-1){
perror("sigaction error");
exit(-1);
}
switch(fork()){
case -1:
perror("fork erro ");
exit(-1);
case 0:
printf("I am child ,PID=%d\n",getpid());
while(1){
sleep(1);
}
_exit(0);
default:
printf("I am parent ,PID=%d\n",getpid());
while(1)
sleep(1);
break;
}
exit(0);
}
运行结果:
可以看到,每次发送SIGINT信号(即按下ctrl+c,向前台进程组发送SIGINT信号),都会响应两侧信号处理函数,一次是父进程响应,一次是子进程响应。
结论二:如果子进程调用exec族函数,则不会继承信号处理函数
验证这个结论需要子进程调用exec族函数去执行另外一个可执行文件,因此要先编写一个测试的程序,把它编译成名字为b.out的可执行文件,这个程序如下:
#include <unistd.h>
#include <stdio.h>
int main(void)
{
while(1){
sleep(1);
}
}
//程序功能很简单,在前台不断打印字符串
然后编写测试程序,如下:
/**
****************************************************************************************
* @file 31_子进程信号处理机制2.c
* @author GuiStar-李什么恩
* @version V1.1.0
* @date 2023-4-22
* @brief fork后如果子进程调用exec族函数,则不会继承信号处理函数
****************************************************************************************
*/
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
static void sig_handler(int sig)
{
printf("Received signal:%d,PID=%d\n",sig,getpid());
}
int main(void)
{
struct sigaction sig={
0
};
int ret;
sig.sa_handler = sig_handler;
sig.sa_flags = 0;
ret = sigaction(SIGINT, &sig, NULL);
if(ret==-1){
perror("sigaction error");
exit(-1);
}
switch(fork()){
case -1:
perror("fork erro ");
exit(-1);
case 0:
printf("I am a child,PID=%d\n",getpid());
execl("./b.out","./b.out",NULL);
_exit(0);
default:
printf("I am parent ,PID=%d\n",getpid());
while(1)
sleep(1);
break;
}
exit(0);
}
运行结果:
可以发现,每次发送SIGINT信号,都只有父进程响应
结论三:无论子进程是否调用exec族函数,都会继承父进程的信号掩码
/**
****************************************************************************************
* @file 33_子进程信号处理机制1.c
* @author GuiStar-李什么恩
* @version V1.1.0
* @date 2023-4-22
* @brief fork后无论子进程是否调用exec族函数,都会继承父进程的信号掩码
****************************************************************************************
*/
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
int main(void)
{
sigset_t sig_set;
int ret;
sigemptyset(&sig_set);
sigaddset(&sig_set,SIGINT);
ret = sigprocmask(SIG_BLOCK,&sig_set,NULL);
if(ret==-1){
perror("sigprocmask error");
exit(-1);
}
switch(fork()){
case -1:
perror("fork erro ");
exit(-1);
case 0:
printf("I am a child,PID=%d\n",getpid());
execl("./b.out","./b.out",NULL);
_exit(0);
default:
printf("I am parent ,PID=%d\n",getpid());
while(1)
sleep(1);
break;
}
exit(0);
}
该程序仍然使用execl函数是子进程执行结论二编写的b.out程序,运行结果如下:
可以看到,多次发送SIGINT信号,父进程和子进程都没有响应(因为没有编写SIGINT的信号处理函数,所以如果响应的话,应该执行系统默认响应:终止程序),说明子进程和父进程都屏蔽了SIGINT信号,也就是说子进程继承了父进程的信号掩码