天天看点

Linux退出进程exit/_exit和等待子进程退出wait函数分析

在说明exit和_exit函数之前,先解释下Linux的缓冲I/O操作。所谓“缓冲I/O”就是对于每一个打开的文件,系统都会在内存中开辟一块区域作为其缓冲区,每次读取文件时,将连续的读取N条记录到这个缓冲区中,这样下次再进行读取操作时,将自动的去读取开辟的这段缓冲区,而不是每次都是读取文件。在写文件时,也只是将内容先写入到这段缓冲区中,当满足某个条件(如达到一定数量或遇到特定字符等),再将缓冲区中的内容一次性写入到文件。这样所带来的好处就是大大提高了效率,不必在读取或写入文件时,每次都对文件进程操作,而是只操作内存。弊端就是有时在写入操作时,我们认为已经将内容写入到文件中了,而实际上还只是存在于内存中,如果此时强行退出,必然会导致文件丢失。

exit函数

头文件:#include <stdlib.h>

函数原型:void exit(int status);

参数说明:status=0表示正常退出,status != 0表示异常退出

函数说明:调用该函数结束一个进程。它检查文件的打开情况,把文件缓冲区的内容写回文件。

示例代码:

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

int main(void)
{
    printf("Use exit to exit\n");
    printf("exit can print me");
    exit(0);

    return 0;
}
           

执行结果:

Use exit to exit

exit can print me

结果分析:printf函数就是使用缓冲I/O的方式,该函数在遇到“\n”换行符时自动的从缓冲区中将记录读出。第二个printf没有用\n说明其没有将数据从缓冲区中读出,此时调用exit函数,将检查缓冲区,发现有数据,所有将数据读出。

_exit函数

头文件:#include <unistd.h>

函数原型:void _exit(int status);

参数说明:status=0表示正常退出,status != 0表示异常退出

函数说明:调用该函数结束一个进程。它不检查文件的打开情况,强行退出进程。

示例代码:

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

int main(void)
{
    printf("Use _exit to exit\n");
    printf("_exit can not print me");
    _exit(0);

    return 0;
}
           

执行结果:

Use _exit to exit

结果分析:调用_exit函数退出进程,不检查缓冲区,所有缓冲区中的数据无法被读出,也就没有打印出_exit can not print me了。

因此为了确保文件操作正常,都使用exit函数。

wait函数

头文件:#include <sys/types.h>

                #include<sys/wait.h>

函数原型:pid_t wait (int * status);

函数参数:int *status子进程结束状态值

返回值:执行成功则返回子进程pid,发发生错误,返回-1。错误原因存于errno 中。

函数说明:调用wait()函数的进程会一直阻塞,直到有信号来到或子进程结束。如果在调用wait()时子进程已经结束,则wait()会立即返回子进程结束状态值。子进程的结束状态值会由参数status 返回,而子进程的进程识别码也会一起返回。如果不在意结束状态值,则参数status 可以设成NULL。

示例代码:

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

int main(void)
{
	pid_t pid = 0;
	pid_t ChildPid = 0;
	int ChildStatus = 0;

	pid = fork();

	if(pid > 0)
	{
		ChildPid = wait(&ChildStatus);
		printf("This is parent progress, my child progess pid = %d, his status = %d\n", ChildPid, ChildStatus);
	}
	else
	{
		printf("This is child progress, my pid = %d\n", getpid());
		sleep(10);
		exit(0);
	}

   return 0;
}
           

执行结果:

This is child progress, my pid = 2312

This is parent progress, my child progess pid = 2312, his status = 0

结果分析:父进程会在wait函数那阻塞,直到子进程结束,也就是说,打印This is child progress, my pid = 2312这句之后,大约要过10s才打印This is parent progress, my child progess pid = 2312, his status = 0。

注意点:如果父进程先于子进程退出,这样会产生孤儿进程,这时候子进程是无法退出的,比如下面这段代码就可能会产生这种情况

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

int main(void)
{
	pid_t pid = 0;
	pid_t ChildPid = 0;
	int ChildStatus = 0;

	pid = fork();

	if(pid > 0)
	{
		printf("This is parent\n");
		exit(0);
	}
	else
	{
		printf("This is child progress, my pid = %d\n", getpid());
		exit(0);
	}

   return 0;
}
           

如果父进程先被执行了,那么父进程退出后,子进程变成了孤儿进程,无法退出,程序会一直卡住。因此一般情况下要求子进程先于父进程退出(在父进程中调用wait函数,或者使用信号),这样就可以防止孤儿进程的产生。

waitpid函数

头文件:同wait函数

函数原型:pid_t waitpid(pid_t pid,int * status,int options);

参数参数:1)pid_t pid:等待的子进程识别码,可取如下值

                        pid<-1 等待进程组识别码为pid 绝对值的任何子进程。

                        pid=-1 等待任何子进程,此时相当于wait()。

                        pid=0  等待进程组识别码与目前进程相同的任何子进程。

                        pid>0  等待任何子进程识别码为pid 的子进程。

                    2)int *status子进程结束状态值

                    3)int options:可以为0或者如下几个取值的“或”

                       WNOHANG 如果没有任何已经结束的子进程则马上返回,不予以等待。

                       WUNTRACED 如果子进程进入暂停执行情况则马上返回,但结束状态不予以理会。

                       子进程的结束状态返回后存于status,底下有几个宏可判别结束情况:

                       WIFEXITED(status)如果子进程正常结束则为非0 值。

                       WEXITSTATUS(status)取得子进程exit()返回的结束代码,一般会先用WIFEXITED 来判断是否正常结束才能使用此宏。

                       WIFSIGNALED(status)如果子进程是因为信号而结束则此宏值为真。

                       WTERMSIG(status)取得子进程因信号而中止的信号代码,一般会先用WIFSIGNALED 来判断后才使用此宏。

                       WIFSTOPPED(status)如果子进程处于暂停执行情况则此宏值为真。一般只有使用WUNTRACED 时才会有此情况。

                       WSTOPSIG(status)取得引发子进程暂停的信号代码,一般会先用WIFSTOPPED 来判断后才使用此宏。

返回值:同wait函数

函数说明:用于等待任何的进程结束,通过设定不同的参数,等待的方式也将不同

示例代码:

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

int main(void)
{
	pid_t pid = 0;
	int status = 0;

	pid = fork();
	
	if(pid < 0)  
	{
		perror("fork()");
		exit(1);
	}
	else if(pid > 0)
	{
		waitpid(pid, &status, 0);
		printf("This is parent, my child's pid = %d\n", pid);
		printf("child process exited with status %d \n", status);
		exit(0);
	}
	else
	{
		printf("This is child, my pid = %d\n", getpid());
		sleep(3);
		exit(0);
	}
	exit(0);
}
           

执行结果:

This is child, my pid = 3051

This is parent, my child's pid = 3051

child process exited with status 0

结果分析:父进程会在waitpid函数那阻塞,直到子进程结束。

初学者笔记,有错误请指出,谢谢!

继续阅读