1.首先讲一下进程的退出
进程的退出分为正常退出和异常退出:
正常退出:
(1)main函数调用return
(2)进程调用exit(),标准C库
(3)进程调用_exit()或_Exit(),属于系统调用
(4)进程最后一个线程返回
(5)最后一个线程调用pthread_exit
异常退出:
(1)调用abort
(2)当进程收到某些信号时,例如Ctrl+c
(3)最后一个线程对取消(cancellation)请求做出响应
以上三张图片是对进程退出的补充,
在第二第三张图中的exit()
,_exit()
和_Exit()
,这三个函数其实一样的,但习惯用exit()
。exit相当于是_exit和_Exit的一个组合。
2.接下来我们介绍一下等待子进程退出以及为什么要等待子进程的退出
在之前我们没学习到等待子进程退出的时候,都是没做等待子进程的操作的,那么这样的子进程都是会变成僵尸进程的。
(1)父进程等待子进程的退出并收集子进程的退出状态,子进程的退出状态不被收集,子进程就会变成僵死进程(僵尸进程)。
介绍一下等待子进程的相关函数:
上图中的waip写错了,应该是wait,waitpid中的有一个选项,可以使调用者不阻塞的选项时options,
这里的阻塞就是相当于vfork方式创建子进程,父进程处于阻塞状态,让子进程先执行,当父进程收到子进程的退出信号的时候,父进程开始执行。而waitpid不阻塞的机制就有点像fork方式创建的子进程一样,父子进程交替执行,所以这就搞的以waitpid的方式等待有点多余了,所以等待子进程退出还是使用wait函数多,但也有场景需要用waitpid的,所以只是少用waitpid而已。
僵尸进程:Z+
正常进程:即有父进程等待的进程,S+
这里的11328就是正常进程,11329就是僵尸进程,有时候调用waitpid函数等待子进程的时候在linux 底下有时候子进程是僵尸进程,有时候又是正常的进程,这以自己跑起来相应的程序查看的为准,而调用wait等待子进程的时候,这时子进程是正常的进程。
(2)以wait函数等待子进程:
直接上代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
int main()
{
pid_t pid;
int status;
int cnt = 0;
pid = fork();
if(pid > 0)
{
wait(&status);
while(1)
{
printf("the process of child is Quit,the status is:%d\n",WEXITSTATUS(status));
printf("the process is father process,pid is:%d\n",pid);
printf("cnt = %d\n",cnt);
sleep(3);
}
}
else if(pid == 0)
{
while(1)
{
cnt++;
printf("the process is child process,pid is:%d\n",getpid());
printf("cnt = %d\n",cnt);
sleep(3);
if(cnt == 3)
{
exit(3);
}
}
}
return 0;
}
结果:
从结果中可以看出,父进程先是等待子进程执行三次接收到子进程的返回后,才开始执行。
在父进程中我们看到一个WEXITSTATUS(status)
,这个就是打印出子进程的返回的状态值,子进程的状态值就是exit括号里的,例如exit(3),状态值就是3。
(3)以waitpid的方式等待子进程
status参数是一个整型指针:
非空:子进程退出状态放在它所指向的地址中;
空:不关心退出状态
此时的pid其实就是此时子进程的ID号,而waitpid是不阻塞机制的,可以看看上面介绍
直接上代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
int main()
{
pid_t pid;
int status;
int cnt = 0;
pid = fork();
if(pid > 0)
{
// wait(&status);
waitpid(pid,&status,WNOHANG);
printf("pid = %d\n",pid);
while(1)
{
// printf("the process of child is Quit,the status is:%d\n",WEXITSTATUS(status));
printf("the process is father process,pid is:%d\n",getpid());
printf("cnt = %d\n",cnt);
sleep(3);
}
}
else if(pid == 0)
{
while(1)
{
cnt++;
printf("the process is child process,pid is:%d\n",getpid());
printf("cnt = %d\n",cnt);
sleep(3);
if(cnt == 3)
{
exit(3);
}
}
}
return 0;
}
结果:
可以看到这里是父进程跟子进程交替执行,当子进程执行三次后退出进程,所以这就是waitpid跟wait的本质区别。
(4)接下来我们来讲一下孤儿进程
孤儿进程:父进程如果不等待子进程退出,在子进程之前就结束了自己的“生命”,此时的子进程就叫做孤儿进程。
而linux避免系统存在过多的孤儿进程,init进程(进程ID号为1)收留孤儿进程,变成孤儿进程的父进程
直接上代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
int main()
{
pid_t pid;
int cnt = 0;
pid = fork();
if(pid > 0)
{
printf("this is father process,pid is:%d\n",getpid());
}
else if(pid == 0)
{
while(1)
{
printf("this is child process,the father is:%d\n",getppid());
sleep(3);
cnt++;
if(cnt == 5)
{
exit(0);
}
}
}
return 0;
}
结果:
从结果中可见除了第一个子进程的父进程的ID号为正常的ID号,下面的那些父进程的ID号都是1,即为init进程。
学习笔记,仅供参考!