大家看看度娘的解释也很清楚,在Linux系统中,正常情况下,父进程创建子进程,他俩运行互相不干涉,独立运行, 父进程没办法预测子进程什么时候结束,做了什么动作。当子进程调用exit命令来结束自己的生命,实际上他并没有真正的被销毁, 内核只是释放了该进程的所有资源,包括打开的文件、占用的内存等,但是留下一个称为僵尸进程的数据结构,这个结构保留了一定的信息(包括进程号 the process ID,退出状态,运行时间),这些信息直到父进程通过 wait()或者waitpid() 来取时才释放。
通过子进程返回的状态符,父进程可以知道子进程的状态信息。
孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。
僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。
僵尸进程不占用任何的内存空间,但是如果父进程不调用wait/waitpid的话, 那么保留的那段信息就不会释放,其进程号会一定被占用,但是系统所能使用的进程号是有限的,如果产生了大量的僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程。
孤儿进程是没有父进程的进程,孤儿进程这个重任就落到了init进程身上,init进程就好像是一个民政局,专门负责处理孤儿进程的善后工作。每当出现一个孤儿进程的时候,内核就把孤 儿进程的父进程设置为init,而init进程会循环地wait()它的已经退出的子进程。这样,当一个孤儿进程凄凉地结束了其生命周期的时候,init进程就会代表党和政府出面处理它的一切善后工作。因此孤儿进程并不会有什么危害。
怎么查看僵死进程,利用命令ps -aux或者ps -aux| grep 执行文件名 过滤,可以看到有标记为Z的进程就是僵死进程。
参考:
https://www.cnblogs.com/Anker/p/3271773.html
进程退出分为正常退出和异常退出(比如我们的ctrlC)
正常退出
1.main函数调用return
2.进程调用exit(),标准C库
3.进程调用 _ exit() 或 者 _ Exit(),属于系统调用
补充:
4.进程最后一个线程返回
5.最后一个线程调用pthread_exit
异常退出
1.调用abort函数
2.当进程收到某些信号时,比如ctrl +C
3.最后一个线程对取消(cancellation)请求做出相应
子进程exit之后如果不进行回收,就会变成僵尸进程,那我们看看是如何回收子进程的呢?
有两种函数分别是wait函数和waitpid函数
#include
#include //头文件
pid_t wait(int *status);//常用
pid_t waitpid(pid_t pid, int *status, int options);
wait 阻塞函数
函数作用:
1.阻塞并等待子进程退出
2.回收子进程残留资源
3.获取子进程结束状态(退出原因)
调用一次只能回收一个子进程
函数原型
pid_t wait( int *status);
返回值:
‐1 : 回收失败,已经没有子进程了
-0 : 回收子进程对应的pid
参数 :
status判断子进程如何退出状态
1.WIFEXITED(status):为非0 ,进程正常结束
WEXITSTATUS(status)宏为真,获取进程退出状态的参数
2.WIFSIGNALED(status):为非0,进程异常退出
WTERMSIG(status)宏为真,取得使进程种植的那个信号的编号
waitpid函数
函数作用:
1.阻塞并等待子进程退出
2.回收子进程残留资源
3.获取子进程结束状态(退出原因)
调用一次只能回收一个子进程
函数原型
pid_t waitpid(pid_t pid, int *status, int options);
参数
PID
pid<-1 等待进程组号为pid绝对值的任何子进程。
pid=-1 等待任何子进程,此时的waitpid()函数就退化成了普通的wait()函数。
pid=0 等待进程组号与目前进程相同的任何子进程,也就是说任何和调用waitpid()函数的进程在同一个进程组的进程。
pid>0 等待进程号为pid的子进程。(一般是这个比较常用)
options
WNOHANG 如果pid指定的子进程没有结束,则waitpid()函数立即返回0,而不是阻塞在这个函数上等待;如果结束了,则返回该子进程的进程号。
(一般是这个比较常用)
WUNTRACED 如果子进程进入暂停状态,则马上返回。
下面看看这个demo
#include
#include
#include
#include
int main(){
pid_t pid;
int count = 0;
int status;
pid =getpid();
pid = fork();
if(pid > 0){
wait(&status);
printf("the status is %d\n",WEXITSTATUS(status));
while(1){
printf("father pid is %d\n",getpid());
sleep(1);
}
}else{
while(1){
printf("child pid is %d\n",getpid());
sleep(1);
count++;
if(count == 5){
exit(3);
}
}
}
return 0;
}
通过这个demo可以看到wait的阻塞功能
我们是用fork,按道理应该是father和child根据系统调度交替执行,但是这里的结果显示,没有。反而是先让child执行完5次,再去执行father,而且通过WEXITSTATUS(status)宏为真,获取进程退出状态的参数这个参数我们也可以获取子进程退出是的状态是3
同样的waitpid相同的使用方法,在这种情况下,waitpid(-1,NULL,0);作用与wait()一样
child pid is 10386
child pid is 10386
child pid is 10386
child pid is 10386
child pid is 10386
the status is 3
father pid is 10385
father pid is 10385
father pid is 10385
father pid is 10385
father pid is 10385
····