目录
孤儿进程
测试代码1
测试结果
僵尸进程
测试代码2
测试结果
wait
参数*wstatus
返回值
测试代码3
测试结果
测试代码4
测试结果
测试代码5
测试结果
waitpid
参数pid
参数*wstatus
参数options
返回值
测试代码6
测试结果
测试代码7
测试结果
测试代码8
测试结果
测试代码9
测试结果
测试代码10
测试结果
孤儿进程
父进程先于子进程结束,则子进程成为孤儿进程,子进程的父进程成为init进程,称为init进程领养孤儿进程。
测试代码1
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
printf("程序开始运行!\n");
printf("当前进程的ID是%d。\n", getpid());
pid_t jin_cheng = fork();
if (jin_cheng < 0)
{
perror("创建进程错误!");
exit(1);
}
else if (jin_cheng == 0)
{
while(1){
printf("这是子进程,当前进程的ID是%d,父进程ID是%d。\n", getpid(),getppid());
sleep(1);
}
}
else if (jin_cheng > 0)
{
sleep(3);
printf("这是父进程,当前进程的ID是%d,子进程ID是%d。\n", getpid(),jin_cheng);
printf("父程序结束!\n");
printf("孤儿进程产生!\n");
}
return 0;
}
测试结果
查看进程的对应父进程。
ps ajx
父进程死后,由1319进程进行收养。
僵尸进程
进程终止,父进程尚未回收,子进程残留资源(PCB)存放于内核中,变成僵尸进程。 僵尸进程不能使用kill命令清除掉的。因为kill命令只是用来终止进程的, 而僵尸进程已经终止。
测试代码2
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
printf("程序开始运行!\n");
printf("当前进程的ID是%d。\n", getpid());
pid_t jin_cheng = fork();
if (jin_cheng < 0)
{
perror("创建进程错误!");
exit(1);
}
else if (jin_cheng == 0)
{
sleep(3);
printf("这是子进程,当前进程的ID是%d,父进程ID是%d。\n", getpid(), getppid());
printf("子进程结束!\n");
printf("僵尸进程产生!\n");
}
else if (jin_cheng > 0)
{
while (1)
{
printf("这是父进程,当前进程的ID是%d,子进程ID是%d。\n", getpid(), jin_cheng);
sleep(1);
}
}
return 0;
}
测试结果
6138进程已死亡,成为僵尸进程,等待父进程进行回收。
想要除掉僵尸进程,可以杀掉父进程。
kill -9 6137
wait
一个进程在终止时会关闭所有文件描述符,释放在用户空间分配的内存,但它的PCB还保留着,内核在其中保存了一些信息:如果是正常终止则保存着退出状态,如果是异常终止则保存着导致该进程终止的信号是哪个。这个进程的父进程可以调用wait或waitpid获取这些信息,然后彻底清除掉这个进程。
父进程调用wait函数可以回收子进程终止信息。该函数有三个功能:
阻塞等待子进程退出,就相当于等待子进程死亡。
回收子进程残留资源。
获取子进程结束状态(退出原因)。
当进程终止时,操作系统的隐式回收机制会:
关闭所有文件描述符。
释放用户空间分配的内存。
内核的PCB仍存在,其中保存该进程的退出状态。可使用wait函数传出参数status来保存进程的退出状态。借助宏函数来进一步判断进程终止的具体原因。
进程正常结束
WIFEXITED(status)!=0
获取进程退出状态 (exit的参数),可以在进程正常结束的情况下使用。
WEXITSTATUS(status)
进程异常终止
WIFSIGNALED(status)!=0
获取使进程终止信号的编号,可以在进程异常终止的情况下使用。
WTERMSIG(status)
进程处于暂停状态
WIFSTOPPED(status)!=0
获取使进程暂停信号的编号,可以在进程暂停的情况下使用。
WSTOPSIG(status)
进程暂停后已经继续运行
WIFCONTINUED(status)!=0
父进程牛逼,不关心子进程死亡原因,参数传NULL。
ZiJinCheng_ID = wait(NULL);
man 2 wait
参数*wstatus
传出参数,回收进程的状态。
返回值
成功:清理掉的子进程ID。
失败:-1,没有子进程。
测试代码3
使用wait回收进程。
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
int i, wstatus;
pid_t ZiJinCheng_ID; //子进程ID
printf("程序开始运行!\n");
printf("当前进程的ID是%d。\n", getpid());
pid_t jin_cheng = fork();
if (jin_cheng < 0)
{
perror("创建进程错误!");
exit(1);
}
else if (jin_cheng == 0)
{
for (i = 0; i < 3; i++)
{
printf("这是子进程,当前进程的ID是%d,父进程ID是%d,延时%d秒。\n", getpid(), getppid(), i + 1);
sleep(1);
}
printf("子进程结束!\n");
printf("僵尸进程产生!\n");
}
else if (jin_cheng > 0)
{
printf("这是父进程,当前进程的ID是%d,子进程ID是%d,父进程开始等待收尸!\n", getpid(), jin_cheng);
ZiJinCheng_ID = wait(&wstatus);
printf("这是父进程,当前进程的ID是%d,子进程ID是%d,父进程收尸完成,僵尸进程ID是%d。\n", getpid(), jin_cheng, ZiJinCheng_ID);
printf("父程序结束!\n");
}
return 0;
}
测试结果
测试代码4
查看子进程正常终止情况。
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
int i, wstatus;
pid_t ZiJinCheng_ID; //子进程ID
printf("程序开始运行!\n");
printf("当前进程的ID是%d。\n", getpid());
pid_t jin_cheng = fork();
if (jin_cheng < 0)
{
perror("创建进程错误!");
exit(1);
}
else if (jin_cheng == 0)
{
for (i = 0; i < 3; i++)
{
printf("这是子进程,当前进程的ID是%d,父进程ID是%d,延时%d秒。\n", getpid(), getppid(), i + 1);
sleep(1);
}
printf("子进程结束!\n");
printf("僵尸进程产生!\n");
return 100;
}
else if (jin_cheng > 0)
{
printf("这是父进程,当前进程的ID是%d,子进程ID是%d,父进程开始等待收尸!\n", getpid(), jin_cheng);
ZiJinCheng_ID = wait(&wstatus); //子进程未结束,父进程阻塞在这个函数上
printf("这是父进程,当前进程的ID是%d,子进程ID是%d,父进程收尸完成,僵尸进程ID是%d。\n", getpid(), jin_cheng, ZiJinCheng_ID);
if (WIFEXITED(wstatus)) //判断子进程是否正常结束
{
printf("子进程正常死亡,死亡原因:%d。\n", WEXITSTATUS(wstatus));
}
if (WIFSIGNALED(wstatus)) //判断子进程是否异常结束
{
printf("子进程被杀,终止信号是%d。\n", WTERMSIG(wstatus));
}
printf("父程序结束!\n");
}
return 0;
}
测试结果
测试代码5
查看子进程异常终止情况。
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
int i, wstatus;
pid_t ZiJinCheng_ID; //子进程ID
printf("程序开始运行!\n");
printf("当前进程的ID是%d。\n", getpid());
pid_t jin_cheng = fork();
if (jin_cheng < 0)
{
perror("创建进程错误!");
exit(1);
}
else if (jin_cheng == 0)
{
while(1)
{
printf("这是子进程,当前进程的ID是%d,父进程ID是%d,我是长生不老的。\n", getpid(), getppid());
sleep(1);
}
}
else if (jin_cheng > 0)
{
printf("这是父进程,当前进程的ID是%d,子进程ID是%d,父进程开始等待收尸!\n", getpid(), jin_cheng);
ZiJinCheng_ID = wait(&wstatus); //子进程未结束,父进程阻塞在这个函数上
printf("这是父进程,当前进程的ID是%d,子进程ID是%d,父进程收尸完成,僵尸进程ID是%d。\n", getpid(), jin_cheng, ZiJinCheng_ID);
if (WIFEXITED(wstatus)) //判断子进程是否正常结束
{
printf("子进程正常死亡,死亡原因:%d。\n", WEXITSTATUS(wstatus));
}
if (WIFSIGNALED(wstatus)) //判断子进程是否异常结束
{
printf("子进程被杀,终止信号是%d,还说是长生不老,直接把你给杀了。\n", WTERMSIG(wstatus));
}
printf("父程序结束!\n");
}
return 0;
}
测试结果
waitpid
指定某一个进程进行回收。
man 2 waitpid
参数pid
大于0:指定回收子进程的ID。
0:回收和当前调用waitpid一个组的所有子进程。
-1:回收任意一个子进程。
小于-1:回收指定进程组内的任意子进程。
参数*wstatus
传出参数,回收进程的状态。
参数options
WNOHANG:指定回收方式为非阻塞。
0:阻塞回收,功能等于wait函数。
返回值
大于0:成功回收子进程的PID。
0:函数调用时,参数options指定了WNOHANG,并且子进程没有结束。
-1:失败。
测试代码6
以不阻塞的方式回收指定的一个进程。父进程不延时,子进程没有结束,返回0。
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
int i;
pid_t temp_ZiJinCheng_ID; //临时子进程ID
pid_t JinCheng_ID; //子进程ID
pid_t HuiShou_JinCheng_ID; //回收进程ID
printf("程序开始运行!\n");
printf("当前进程的ID是%d。\n", getpid());
printf("开始创建进程!\n");
for (i = 0; i < 5; i++)
{
temp_ZiJinCheng_ID = fork();
if (temp_ZiJinCheng_ID == 0) //子进程,不参与创建进程
{
break;
}
if (i == 2)
{
JinCheng_ID = temp_ZiJinCheng_ID;
}
}
if (i == 5)
{
HuiShou_JinCheng_ID=waitpid(JinCheng_ID,NULL,WNOHANG);//以非阻塞的方式回收一个进程ID,JinChen_ID
if(HuiShou_JinCheng_ID<0){
perror("回收进程错误");
exit(1);
}
printf("这是父进程,当前进程的ID是%d,回收的子进程是%d。\n", getpid(),HuiShou_JinCheng_ID);
}
else
{
sleep(i); //延时i秒,按顺序输出
printf("这是第%d个子进程,当前进程的ID是%d。\n", i + 1, getpid());
}
return 0;
}
测试结果
测试代码7
以不阻塞的方式回收指定的一个进程。父进程延时,子进程结束,返回回收的进程ID。
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
int i;
pid_t temp_ZiJinCheng_ID; //临时子进程ID
pid_t JinCheng_ID; //子进程ID
pid_t HuiShou_JinCheng_ID; //回收进程ID
printf("程序开始运行!\n");
printf("当前进程的ID是%d。\n", getpid());
printf("开始创建进程!\n");
for (i = 0; i < 5; i++)
{
temp_ZiJinCheng_ID = fork();
if (temp_ZiJinCheng_ID == 0) //子进程,不参与创建进程
{
break;
}
if (i == 2)
{
JinCheng_ID = temp_ZiJinCheng_ID;
}
}
if (i == 5)
{
sleep(5); //延时,最后输出父进程
HuiShou_JinCheng_ID = waitpid(JinCheng_ID, NULL, WNOHANG); //以非阻塞的方式回收一个进程ID,JinChen_ID
if (HuiShou_JinCheng_ID < 0)
{
perror("回收进程错误");
exit(1);
}
printf("这是父进程,当前进程的ID是%d,回收的子进程是%d。\n", getpid(), HuiShou_JinCheng_ID);
}
else
{
sleep(i); //延时i秒,按顺序输出
printf("这是第%d个子进程,当前进程的ID是%d。\n", i + 1, getpid());
}
return 0;
}
测试结果
测试代码8
回收一个任意子进程。
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
int i;
pid_t temp_ZiJinCheng_ID; //临时子进程ID
pid_t HuiShou_JinCheng_ID; //回收进程ID
printf("程序开始运行!\n");
printf("当前进程的ID是%d。\n", getpid());
printf("开始创建进程!\n");
for (i = 0; i < 5; i++)
{
temp_ZiJinCheng_ID = fork();
if (temp_ZiJinCheng_ID == 0) //子进程,不参与创建进程
{
break;
}
}
if (i == 5)
{
sleep(6); //延时,确保所有子进程都已经结束,最后输出父进程
HuiShou_JinCheng_ID = waitpid(-1, NULL, WNOHANG); //以非阻塞的方式回收任意一个子进程ID
if (HuiShou_JinCheng_ID < 0)
{
perror("回收进程错误");
exit(1);
}
printf("这是父进程,当前进程的ID是%d,回收的子进程是%d。\n", getpid(), HuiShou_JinCheng_ID);
}
else
{
sleep(i); //延时i秒,按顺序输出
printf("这是第%d个子进程,当前进程的ID是%d。\n", i + 1, getpid());
}
return 0;
}
测试结果
网上教程说是回收任意一个子进程,但是从结果来看,每次都是回收第一个结束的子进程,不排除较新版本的Ubuntu系统优化了回收进程的算法,实现先结束先回收的情况。
测试代码9
以阻塞的方式回收全部死亡的子进程。
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
int i;
pid_t temp_ZiJinCheng_ID; //临时子进程ID
pid_t HuiShou_JinCheng_ID; //回收进程ID
printf("程序开始运行!\n");
printf("当前进程的ID是%d。\n", getpid());
printf("开始创建进程!\n");
for (i = 0; i < 5; i++)
{
temp_ZiJinCheng_ID = fork();
if (temp_ZiJinCheng_ID == 0) //子进程,不参与创建进程
{
break;
}
}
if (i == 5)
{
while ((HuiShou_JinCheng_ID = waitpid(-1, NULL, 0)) != -1) //以阻塞的方式回收任意一个子进程ID
{
printf("这是父进程,当前进程的ID是%d,回收的子进程是%d。\n", getpid(), HuiShou_JinCheng_ID);
}
if (HuiShou_JinCheng_ID < 0)
{
perror("这是父进程,回收子进程完成,父进程结束!\n");
}
}
else
{
sleep(i); //延时i秒,按顺序输出
printf("这是第%d个子进程,当前进程的ID是%d。\n", i + 1, getpid());
}
return 0;
}
测试结果
测试代码10
以非阻塞的方式回收全部死亡的子进程。
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
int i;
pid_t temp_ZiJinCheng_ID; //临时子进程ID
pid_t HuiShou_JinCheng_ID; //回收进程ID
printf("程序开始运行!\n");
printf("当前进程的ID是%d。\n", getpid());
printf("开始创建进程!\n");
for (i = 0; i < 5; i++)
{
temp_ZiJinCheng_ID = fork();
if (temp_ZiJinCheng_ID == 0) //子进程,不参与创建进程
{
break;
}
}
if (i == 5)
{
sleep(6); //延时,确保所有的子进程都死亡
while ((HuiShou_JinCheng_ID = waitpid(-1, NULL, WNOHANG)) != -1) //以非阻塞的方式回收任意一个子进程ID
{
printf("这是父进程,当前进程的ID是%d,回收的子进程是%d。\n", getpid(), HuiShou_JinCheng_ID);
}
if (HuiShou_JinCheng_ID < 0)
{
perror("这是父进程,回收子进程完成,父进程结束!\n");
}
}
else
{
sleep(i); //延时i秒,按顺序输出
printf("这是第%d个子进程,当前进程的ID是%d,该进程结束。\n", i + 1, getpid());
}
return 0;
}
测试结果