您的位置:首页 > 运维架构 > Linux

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

2013-04-04 21:31 323 查看
在说明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函数那阻塞,直到子进程结束。

初学者笔记,有错误请指出,谢谢!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: