子进程对父进程信号的继承情况详细分析(信号)【linux】(zs)

子进程对父进程信号的继承情况

父进程fork出子进程时,子进程会继承父进程很多的属性,其中就包括信号。

fork创建子进程,但是没有exec加载新程序时

在fork子进程之前,如果父进程调用signal设置了某个信号的处理方式的话,那么fork出的子进程会继承父进程对该信号设置的处理方式,比如fork前,父进程将SIGINT设置为了捕获或者忽略,子进程将继承设置的这个处理方式。

父进程将信号的处理方式设置为捕获时,捕获函数对子进程也是有效的。
代码演示:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>


int main()
{
        pid_t ret = 0;
        ret = fork();
        //signal(SIGINT,SIG_IGN);  //把终止进程(ctrl+c)信号转换为忽略信号
        if(ret>0)
        {

        }
        else if(ret == 0)
        {

        }
        while(1);
        return 0;
}

我们可以看到现在父子进程都运行起来了:

在这里插入图片描述

在这里插入图片描述

这个时候两个进程都已经把SIGINT信号修改为SIG_IGN信号

我们可以看到这个时候是没有办法使用ctrl+c进行进程终止操作的。

在这里插入图片描述

由于父进程在fork之前进行了signal函数设置,所以子进程的SIGINT信号也是忽略,所以两个进程都忽略了,两个进程仍然处于运行状态。

接下来我们进行修改,把信号修改为捕获:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>


void signal_fun(int signal)
{
        printf("PID:%d,signal = %d\n",getpid(),signal);
}
int main()
{
        pid_t ret = 0;
        ret = fork();
        //signal(SIGINT,SIG_IGN);  //把终止进程(ctrl+c)信号转换为忽略信号
        signal(SIGINT,signal_fun);  //把终止进程(ctrl+c)信号转换为捕获信号
        if(ret>0)
        {

        }
        else if(ret == 0)
        {

        }
        while(1);
        return 0;
}

运行结果为:
在这里插入图片描述我们可以看到两个进程都在运行。

这和时候我们通过ctrl+c发送信号,结果如下:

在这里插入图片描述

父进程和子进程的处理函数都去调用signal_fun函数,子进程会继承处理方式。并且信号出来函数对于子进程也是有效的。

接下来我们还原为默认处理方式:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>


void signal_fun(int signal)
{
        printf("PID:%d,signal = %d\n",getpid(),signal);
}
int main()
{
        pid_t ret = 0;
        ret = fork();
        //signal(SIGINT,SIG_IGN);  //把终止进程(ctrl+c)信号转换为忽略信号
        //signal(SIGINT,signal_fun);  //把终止进程(ctrl+c)信号转换为捕获信号
        signal(SIGINT,SIG_DFL);  //把终止进程(ctrl+c)设置为默认处理方式
        if(ret>0)
        {

        }
        else if(ret == 0)
        {

        }
        while(1);
        return 0;
}

运行结果为:
在这里插入图片描述我们可以看到退出之后父进程和子进程都退出了:
在这里插入图片描述

SIGINT信号会发送给父子进程。

再次强调,只有在fork之前,父进程所设置的信号处理方式,才会被子进程继承。

为什么捕获函数在子进程里面依然有效?
因为子进程复制了父进程的代码和数据,子进程自然也会包含信号处理函数的代码,所在子进程中依然有效。

子进程当然可以自己调用signal函数,修改掉所继承的处理方式。

代码演示:我们在父进程设置为默认,子进程设置为捕获:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>


void signal_fun(int signal)
{
        printf("PID:%d,signal = %d\n",getpid(),signal);
}
int main()
{
        pid_t ret = 0;
        ret = fork();
        //signal(SIGINT,SIG_IGN);  //把终止进程(ctrl+c)信号转换为忽略信号
        //signal(SIGINT,signal_fun);  //把终止进程(ctrl+c)信号转换为捕获信号
        //signal(SIGINT,SIG_DFL);  //把终止进程(ctrl+c)设置为默认处理方式
        if(ret>0)
        {
                signal(SIGINT,SIG_DFL);  //把终止进程(ctrl+c)设置为默认处理方式
        }
        else if(ret == 0)
        {
        signal(SIGINT,signal_fun);  //把终止进程(ctrl+c)信号转换为捕获信号
        }
        while(1);
        return 0;
}

运行结果为;

在这里插入图片描述我们可以看到父子进程现在都在运行:

在这里插入图片描述我们现在按下ctrl+c:
运行结果为:
在这里插入图片描述

这个时候我们进行进程查看的时候:
在这里插入图片描述

父进程已经终止,但是子进程只是捕获信号进行打印,并没有退出,所以父子进程的处理方式不同。

那如果父进程是在if(ret > 0){}里面设置得呢?
这就是父进程自己的设置,跟子进程没有关系。

代码演示:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>


void signal_fun(int signo)
{
	printf("PID:%d, signo=%d\n", getpid(), signo);
	
}

int main(int argc, char **argv, char **environ)
{
	pid_t ret = 0;
	
	//signal(SIGINT, SIG_IGN);
	//signal(SIGINT, signal_fun);
	//signal(SIGINT, SIG_DFL);


	ret = fork();
	if(ret > 0)
	{
		signal(SIGINT, signal_fun);
	}
	else if(ret == 0)
	{
		
	}
	
	while(1);
	
	return 0;
}

运行结果为:
在这里插入图片描述

这个时候我们可以看到:

在这里插入图片描述

父进程调用捕获函数,子进程默认退出就变成了僵尸进程。

当有调用exec加载新程序时

情况一

fork之前,父进程设置的处理方式是忽略 或 默认时exec加载新程序后,忽略和默认设置依然有效。
我们在父进程fork之前把SIGINT设置为忽略。

代码演示:
signal_exec.c 文件代码:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>


void signal_fun(int signal)
{
        printf("PID:%d,signal = %d\n",getpid(),signal);
}
int main(int argc,char **argv,char ** environ)
{
        pid_t ret = 0;
        ret = fork();
        signal(SIGINT,SIG_IGN);  //把终止进程(ctrl+c)信号转换为忽略信号
        //signal(SIGINT,signal_fun);  //把终止进程(ctrl+c)信号转换为捕获信号
        //signal(SIGINT,SIG_DFL);  //把终止进程(ctrl+c)设置为默认处理方式
        if(ret>0)
        {
        }
        else if(ret == 0)
        {
                execve("./new_signal",argv,environ);
        }
        while(1);
        return 0;
}

new_signal.c文件内容为:

#include <stdio.h>
#include <stdlib.h>
int main()
{
        while(1);
        return 0;
}

运行结果为:
在这里插入图片描述在这里插入图片描述我们可以看到自己进程还是在运行,都没有结束。

我们再还原为默认方式:

signal_exec.c 文件代码:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>


void signal_fun(int signal)
{
        printf("PID:%d,signal = %d\n",getpid(),signal);
}
int main(int argc,char **argv,char ** environ)
{
        pid_t ret = 0;
        ret = fork();
        //signal(SIGINT,SIG_IGN);  //把终止进程(ctrl+c)信号转换为忽略信号
        //signal(SIGINT,signal_fun);  //把终止进程(ctrl+c)信号转换为捕获信号
        //signal(SIGINT,SIG_DFL);  //把终止进程(ctrl+c)设置为默认处理方式
        if(ret>0)
        {
        }
        else if(ret == 0)
        {
                execve("./new_signal",argv,environ);
        }
        while(1);
        return 0;
}

new_signal.c文件内容为:

#include <stdio.h>
#include <stdlib.h>
int main()
{
        while(1);
        return 0;
}

运行结果为:

在这里插入图片描述在这里插入图片描述父子进程都不存在了。

情况二

fork之前,父进程设置处理方式是捕获时
新程序的代码会覆盖子进程中原有的父进程的代码,信号捕获函数的代码也会被覆盖,既然捕获函数已经不存在了,捕获处理方式自然也就没有意义了,所以信号的处理方式会被还原为默认处理方式。
终之,如果子进程所继承的信号处理方式是捕获的话,exec加载新程序后,捕获处理方式会被还原为默认处理方式。
代码演示:
我们在父进程fork之前把信号设置为捕获函数:

signal_exec.c 文件代码:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>


void signal_fun(int signal)
{
        printf("PID:%d,signal = %d\n",getpid(),signal);
}
int main(int argc,char **argv,char ** environ)
{
        pid_t ret = 0;
        ret = fork();
        //signal(SIGINT,SIG_IGN);  //把终止进程(ctrl+c)信号转换为忽略信号
        signal(SIGINT,signal_fun);  //把终止进程(ctrl+c)信号转换为捕获信号
        //signal(SIGINT,SIG_DFL);  //把终止进程(ctrl+c)设置为默认处理方式
        if(ret>0)
        {
        }
        else if(ret == 0)
        {
                execve("./new_signal",argv,environ);
        }
        while(1);
        return 0;
}

new_signal.c文件内容为:

#include <stdio.h>
#include <stdlib.h>
int main()
{
        while(1);
        return 0;
}

运行结果为:
在这里插入图片描述

在这里插入图片描述当我们按下ctrl+c之后结果为:

在这里插入图片描述在这里插入图片描述我们可以看到父进程调用信号捕获函数,打印出来PID和信号值,子进程已经终止,所以子进程就变成了僵尸进程。

如果想让新程序去捕获某个信号只能在新程序里面独立的设置。
代码演示:

signal_exec.c文件内容为:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>


void signal_fun(int signal)
{
        printf("PID:%d,signal = %d\n",getpid(),signal);
}
int main(int argc,char **argv,char ** environ)
{
        pid_t ret = 0;
        ret = fork();
        //signal(SIGINT,SIG_IGN);  //把终止进程(ctrl+c)信号转换为忽略信号
        signal(SIGINT,signal_fun);  //把终止进程(ctrl+c)信号转换为捕获信号
        //signal(SIGINT,SIG_DFL);  //把终止进程(ctrl+c)设置为默认处理方式
        if(ret>0)
        {
        }
        else if(ret == 0)
        {
                execve("./new_signal",argv,environ);
        }
        while(1);
        return 0;
}

new_signal.c 文件内容为:

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>


void signal_fun(int signo)
{
        printf("new_pro pid:%d, signo=%d\n", getpid(), signo);
}

int main(void)
{

        signal(SIGINT, signal_fun);

        while(1);

        return 0;
}

运行结果为:
在这里插入图片描述

在这里插入图片描述父子进程都设置为捕获。

总结

仅fork时子进程会继承父进程fork之前所设置的信号处理方式。

当有exec加载新程序时
(1)子进程继承的处理方式是忽略 或 默认处理方式时,exec新程序后设置依然有效。
(2)如果子进程继承是捕获处理方式时,exec新程序后将被还原为默认处理方式。

发布了163 篇原创文章 · 获赞 94 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_43648751/article/details/104623880
今日推荐