在 Linux 中,共有 8 种进程的退出方法,包括 5 种正常的方法和 3 种异常的方法。
通常来说 Linux 的应用代码会调用 exit 系列函数来退出一个进程。
#include
#include
void exit(int status);
void _exit(int status);
void _Exit(int status);
exit 系列函数没有返回值,使用一个终止状态的整型变量作为参数,Linux 内核会对这个终止状态进行检查:当异常终止时,Linux 内核会直接产生一个终止状态字,描述异常终止的原因,可以通过 wait 或者 waitpid 函数来获得终止状态字;父进程也可以通过检查终止状态来获得子进程的状态。
如果是以下三种状态:
则 Linux 会认为该进程的终止状态是未定义的。
如果 main 函数的返回值定义为整型并且 main 函数是执行到最后一条语句返回, 则该进程的终止状态是0。
在 main 函数中调用 return 语句返回,绝大多数等效于调用 exit 系列函数。
这两个函数的最大区别在于:前者在调用之前要检查文件的打开情况,把文件缓冲区中的内容写回文件;后者直接使进程停止运行,清除其使用的内存空间,销毁其在内核中的各种数据结构,即图中的 “清理 I/O 缓冲” 一项。
当一个进程使用 exit 系列函数退出时,会在内存中保留部分数据以供父进程查询,同时也会产生一个终止状态字,然后 Linux 内核会发出一个SIGCHLD 信号通知父进程,因为子进程的结束对于父进程是异步的,所以这个 SIGCHLD 信号对于父进程也是异步的,父进程可以不响应。
父进程对于退出之后的子进程的默认状态是不处理的,但是这样会导致系统中的僵尸进程浪费了系统资源,此时应该调用 wait 函数或 waitpid 函数对这些僵尸进程进行处理。
在调用 wait 或者 waitpid 函数之后可能存在如下三种情况:
对 wait 和 waitpid 函数的标准调用格式说明如下:
#include
#include
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);
wait 函数返回的终止状态的宏
宏 | 说明 |
---|---|
WIFEXITED(status) | 当子进程正常结束时返回为真 |
WIFSIGNALED(status) | 当子进程异常结束时返回为真 |
WEXITSTATUS(status) | 当WIFEXITED(status)为真时调用,返回状态字的低8位 |
WTERMSIG(status) | 当WIFSIGNALED(status)为真时调用,返回引起终止的信号代号 |
在使用 wait 函数的时候,如果父进程的任何一个子进程返回则 wait 函数返回,而 waitpid 函数可以通过参数来指定需要等待的子进程。
waitpid 函数的参数 pid 用于对子进程进行相应的筛选。
waitpid 函数的参数 options 用于进一步控制 waitpid 函数的操作,其可以是0,也可以是 WNOHANG 和 WUNTRACED 两个选项之一,或者是使用 “I” 符号连接的“或”操作。
对于 waitpid 函数而言,如果指定的进程或者进程组不存在,或者参数 pid 指定的进程不是父进程所调用的子进程,都会出错。
总体而言,waitid 函数提供了 wait 函数所没有的如下三个功能:
使用 waitpid 函数退出进程
#include #include
#include
#include
#include
#include
int main()
{
pid_t pid;
if((pid=fork())<0) //创建子进程失败
{
perror("创建子进程失败!\n");
exit(0);
}
else if(pid==0) //进入子进程
{
if((pid=fork())<0)
{
perror("创建子进程失败!\n");
exit(0);
}
else if(pid>0)
{
exit(0);
}
else
{
sleep(2);
printf("这是第二个子进程,parent pid=%d\n",getppid());
exit(0);
}
}
if(waitpid(pid,NULL,0)!=pid)
{
perror("waitpid 销毁进程失败!\n");
exit(0);
}
exit(0);
}
编译运行,可以看到在退出了第2个子进程后会停止运行一直等待,此时可以使用 ctrl + c 退出。
[cassie@localhost 练习]$ gcc waitpid.c -o waitpid
[cassie@localhost 练习]$ ./waitpid
[cassie@localhost 练习]$ 这是第二个子进程,parent pid=1
^C