✅<1>主页::我的代码爱吃辣
<2>知识讲解:Linux——进程等待
☂️<3>开发环境:Centos7
<4>前言:之前讲过,子进程退出,父进程如果不管不顾,就可能造成‘僵尸进程’的问题,进而造成内存泄漏。另外,进程一旦变成僵尸状态,那就刀枪不入,“杀人不眨眼”的kill -9 也无能为力,因为谁也没有办法杀死一个已经死去的进程。父进程通过进程等待的方式,回收子进程资源。
目录
一.进程退出场景
二.进程常见的退出方法
1. 从main返回
2. 调用exit
3.系统调用 _exit
4.对比exit()和_exit()
5.ctrl + c,信号终止
三.进程等待
1.进程等待的必要性
2.进程等待的方法wait方法
3.进程等待的方法wait方法waitpid方法
四.获取子进程的status
1.代码正常终止
2.代码异常终止
见一见这三种情况:
测试代码程序结束结果正确:
正常终止(可以通过 echo $? 查看进程退出码)
#include
int main()
{
printf("***********begin**********\n");
int sum = 0;
for (int i = 1; i <= 100; i++)
sum += i;
printf("***********end**********\n");
if (sum == 5050)
return 0;
else
return 1;
}
makefile文件:
proc:proc.c
gcc -o $@ $^ -std=c99
.PHONY:clean
clean:
rm -rf proc
测试结果:
测试代码代码运行完毕,结果不正确:
#include
int main()
{
printf("***********begin**********\n");
int sum = 0;
for (int i = 1; i < 100; i++)
sum += i;
printf("***********end**********\n");
if (sum == 5050)
return 0;
else
return 1;
}
测试结果:
测试代码程序异常终止:
注意:进程的退出码,只有八个比特位表示,如果我们返回的是-1,查询的推出吗是255.
我们上述展示的:测试代码程序结束结果正确和测试代码代码运行完毕,结果不正确,都是从main返回的示例。
测试代码:
#include
#include
int main()
{
printf("***********begin**********\n");
int sum = 0;
for (int i = 1; i < 100; i++)
sum += i;
exit(100);
printf("***********end**********\n");
if (sum == 5050)
return 0;
else
return 1;
}
测试结果:
测试代码:
#include
#include
int main()
{
printf("***********begin**********\n");
int sum = 0;
for (int i = 1; i <= 100; i++)
sum += i;
_exit(1000);
printf("***********end**********\n");
if (sum == 5050)
return -1;
else
return 1;
}
测试结果:
测试代码:
#include
#include
#include
int main()
{
printf("hello world");
exit(0);
return 0;
}
我们将exit换成_exit:
#include
#include
#include
int main()
{
printf("hello world");
_exit(0);
return 0;
}
测试结果:
注意:
注意:
return是一种更常见的退出进程方法。执行return n等同于执行exit(n),因为调用main的运行时函数会将main的返回值当做 exit的参数。
通过信号的方式直接终止一个进程。
ctrl + c 就是使用 kill -9 信号直接终止进程。
测试代码:
#include
#include
#include
int main()
{
while (1)
{
printf("hello world\n");
sleep(1);
}
return 0;
}
#include
#include
pid_t wait(int*status);
返回值:
参数:
见一见僵尸状态:
测试代码:
#include
#include
#include
#include
#include
int main()
{
int id = fork();
if (id == 0) // 子进程
{
int n = 5;
while (n--)
{
printf("我是一个子进程,我的pid:%d,我的ppid:%d\n", getgid(), getppid());
sleep(1);
}
return 0;
}
else if (id > 0) // 父进程
{
int n = 10;
while (n--)
{
printf("我是一个父进程,我的pid:%d,我的ppid:%d\n", getgid(), getppid());
sleep(1);
}
}
wait(NULL);
return 0;
}
测试结果:
在子进程退出以后,出现了僵尸状态。
回收僵尸状态:
#include
#include
#include
#include
#include
int main()
{
int id = fork();
if (id == 0) // 子进程
{
int n = 5;
while (n--)
{
printf("我是一个子进程,我的pid:%d,我的ppid:%d\n", getgid(), getppid());
sleep(1);
}
return 0;
}
else if (id > 0) // 父进程
{
wait(NULL);//子进程一旦退出就立马回收
int n = 10;
while (n--)
{
printf("我是一个父进程,我的pid:%d,我的ppid:%d\n", getgid(), getppid());
sleep(1);
}
}
return 0;
}
测试结果:
说明:
pid_ t waitpid(pid_t pid, int *status, int options);
返回值:
参数:
pid:
status:
options:
阻塞时等待任意一个子进程:
#include
#include
#include
#include
#include
int main()
{
int status;
int id = fork();
if (id == 0) // 子进程
{
int n = 5;
while (n--)
{
printf("我是一个子进程,我的pid:%d,我的ppid:%d\n", getgid(), getppid());
sleep(1);
}
return 0;
}
else if (id > 0) // 父进程
{
waitpid(-1, &status, 0);
int n = 10;
while (n--)
{
printf("我是一个父进程,我的pid:%d,我的ppid:%d\n", getgid(), getppid());
sleep(1);
}
}
return 0;
}
测试结果:
非阻塞式等待任意一个子进程:
#include
#include
#include
#include
#include
int main()
{
int status;
int id = fork();
if (id == 0) // 子进程
{
int n = 5;
while (n--)
{
printf("我是一个子进程,我的pid:%d,我的ppid:%d\n", getgid(), getppid());
sleep(1);
}
return 0;
}
else if (id > 0) // 父进程
{
int n = 10;
while (n--)
{
pid_t pid = waitpid(-1, &status, WNOHANG);
if (pid == 0)
{
printf("子进程还未结束\n");
}
else
{
printf("子进程已经结束!!!\n");
}
printf("我是一个父进程,我的pid:%d,我的ppid:%d\n", getgid(), getppid());
sleep(1);
}
}
return 0;
}
测试结果:
说明:子进程还未结束时,父进程仍然可以自己执行,不受任何影响,如果父进程等待到子进程已经退出,就会回收子进程的僵尸状态。
- 如果子进程已经退出,调用wait/waitpid时,wait/waitpid会立即返回,并且释放资源,获得子进程退出信息。
- 如果在任意时刻调用wait/waitpid,子进程存在且正常运行,则进程可能阻塞。
- 如果不存在该子进程,则立即出错返回。
wait和waitpid,都有一个status参数,该参数是一个输出型参数,由操作系统填充。
如果传递NULL,表示不关心子进程的退出状态信息。
否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程。
status不能简单的当作整形来看待,可以当作位图来看待,具体细节如下图(只研究status低16比特位):
注意:
#include
#include
#include
#include
#include
int main()
{
int status;
pid_t id = fork();
if (id == 0) // 子进程
{
int n = 5;
while (n--)
{
printf("我是一个子进程,我的pid:%d,我的ppid:%d\n", getpid(), getppid());
sleep(1);
}
return 100;
}
else if (id > 0) // 父进程
{
int n = 10;
while (n--)
{
pid_t ret = waitpid(-1, &status, WNOHANG);
if (ret == 0)
{
printf("子进程还未结束\n");
}
else // ret > 0
{
if ((status & 0x7F) == 0) // 后七位结果为0,没有收到信号,正常退出。
{
printf("子进程正常结束!!!子进程pid:%d,退出码:%d\n", ret, (status >> 8) & 0x7F);
}
else
{
printf("子进程异常退出子进程pid:%d,收到信号:%d\n", ret, (status) & 0x3F);
}
sleep(1);
exit(0);
}
sleep(1);
printf("我是一个父进程,我的pid:%d,我的ppid:%d\n", getpid(), getppid());
}
}
return 0;
}
测试结果:
#include
#include
#include
#include
#include
int main()
{
int status;
pid_t id = fork();
if (id == 0) // 子进程
{
int n = 15;
while (n--)
{
printf("我是一个子进程,我的pid:%d,我的ppid:%d\n", getpid(), getppid());
sleep(1);
}
return 100;
}
else if (id > 0) // 父进程
{
int n = 10;
while (n--)
{
pid_t ret = waitpid(-1, &status, WNOHANG);
if (ret == 0)
{
printf("子进程还未结束\n");
}
else // ret > 0
{
if ((status & 0x7F) == 0) // 后七位结果为0,没有收到信号,正常退出。
{
printf("子进程正常结束!!!子进程pid:%d,退出码:%d\n", ret, (status >> 8) & 0x7F);
}
else
{
printf("子进程异常退出子进程pid:%d,收到信号:%d\n", ret, (status) & 0x3F);
}
sleep(1);
exit(0);
}
sleep(1);
printf("我是一个父进程,我的pid:%d,我的ppid:%d\n", getpid(), getppid());
}
}
return 0;
}
注意:如果我们在提取退出码时,不想使用位运算,我们可以直接使用: