Linux系统编程与网络编程——进程回收,父进程捕获子进程的退出(八)

终止进程以及进程返回值

exit() 函数的功能是终止调用的进程。它的函数原型如下:
在这里插入图片描述
exit()函数会调用系统调用_exit,立即终止调用的进程。所有属于该进程的文件描述符都关闭。该进程的所有子进程由进程init接收,并对该进程的父进程发出一个SIGCHLD(子进程僵死)的信号。参数status作为退出的状态值返回父进程,该值可以通过系统调用wait()来收集。


等待进程
子进程终止的时候,必须由父进程回收其进程表task_struct,否则进程将处于僵尸状态直到被回收。如果父进程在子进程终止前已经终止,那么该进程的所有子进程由init进程回收

下面我们来看一个例子,如果子进程终止而没有被回收的状态。

源代码: zombi_test.c

#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>

int main(int argc, char** argv)
{
	int ret;
	pid_t pid;
	pid = fork();
	if(pid == 0)
	{
		printf("child PID [%d] will exit now.\n", getpid());
		exit(0);
	}
	else if(pid != -1)
	{
		sleep(60);
	}
	else
	{
		printf("fork error\n");
	}
	return 0;
}

程序执行屏幕输出:
在这里插入图片描述
然后到另外一个终端查看进程:
在这里插入图片描述
可见这时zombie_test处于“Z”的僵尸状态。当过30秒后,父进程退出,我们再查看子进程时,发现子进程已经被回收了。原来当父进程退出的时候,子进程将被移交给initinit进程回收了这个僵尸进程

系统调用wait()的功能是发出调用的进程只要有子进程,就睡眠直到有一个子进程终止。它的函数原型是:
在这里插入图片描述
wait()函数将使进程进入睡眠直到它的一个子进程退出时或收到一个不能被忽略的信号唤醒。如果调用时,已经有退出的子进程,该调用立即返回。其中调用返回时参数status中包含子进程退出时的状态信息返回值是退出的子进程的PID。如果参数status是NULL,那么返回值将被忽略。

检查子程序返回状态的宏定义。
在这里插入图片描述
源代码: wait_test.c

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

int main(int argc, char** argv)
{
	int ret;
	pid_t pid;
	pid_t pid_c;
	pid = fork();
	if(pid == 0)
	{
		printf("child PID [%d] will exit with status 0.\n", getpid());
		exit(0);
	}
	else if(pid == -1)
	{
		pid_c = wait(&ret);
		printf("child prosss PID [%d] return [%d].\n", pid_c, ret);
	}
	else
	{
		printf("error fork\n");
	}
	return 0;
}

执行这个函数,结果如下:
在这里插入图片描述
另外一个等待函数waitpid,函数原型如下:
在这里插入图片描述
waitpid()函数与调用wait()的区别时waitpid()等待由参数pid指定的子进程退出。其中参数pid的含义与取值方法如下表:
在这里插入图片描述
waitpid() options参数可以使用以下的宏或运算获得,如果都不要这些标志可以直接填0
在这里插入图片描述
waitpid()将返回退出的子进程的PID。如果设置了WNOHANG选项没有子进程退出就返回0。如果发生错误时返回-1,发生错误时,可能设置的错误代码如下。
在这里插入图片描述
检查子程序返回状态的宏定义。
在这里插入图片描述
下面的例子用了waitpid()来等待子进程,并用宏定义来判断返回值。

源代码: waitpid_test.c

#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>

int main(int argc, char** argv)
{
	int status;
	pid_t pid;
	pid_t pid_c;
	pid = fork();
	if(pid == 0)
	{
		printf("child process\n");
		sleep(5);
		printf("child PID = [%d] \n", getpid());
		exit(3);
	}
	else if(pid != -1)
	{
		do
		{
			pid_c = waitpid(pid, &status, WNOHANG);
			sleep(1);
		}
		while(pid_c == 0); /*如果返回0代表没有子进程退出*/
		if(WIFEXITED(status))
		printf("child process exit code = [%d]\n", WEXITSTATUS(status));
	}
	else
	{
		printf("fork error\n");
	}
	return 0;
}

这个例子中,我们让子进程先睡眠5秒然后退出,父进程等待函数waitpid()的option使用了WNOHANG选项,父进程不会被阻塞在waitpid()函数。用一个while循环每间隔1秒钟就等待一次子进程的退出。

僵尸进程: 子进程退出,父进程没有回收子进程资源(PCB),则子进程变成僵尸进程。
孤儿进程: 父进程先于子进程结束,则子进程成为孤儿进程,孤儿进程将会被1号进程init收养。

猜你喜欢

转载自blog.csdn.net/Gsunshine24/article/details/88982197