Linux 进程状态:
进程状态 | R | TASK_RUNNING |
可中断状态 | S | TASK_INTERRUPTIBLE |
不可中断睡眠状态 | D | TASK_UNINTERRUPTIBLE |
暂停状态 | T | TASK_STOPPED |
僵尸状态 | Z | TASK_COMBIE |
退出状态 | X | TASK_DEAD |
让我们来看一个示例图:
我们来实现一个僵尸状态
#include <iostream>
#include <sys/types.h>
#include <unistd.h>
using namespace std;
// main函数启动,程序主进程
int main()
{
int number = 100;
// fork作用:创建新的子进程
// 一次调用 ,两次返回
pid_t pid = fork();
if (pid == 0)
{
for (int x = 0; x < 5; x++)
{
cout << "子 getpid=" << getpid() << " x=" << x << endl;
sleep(1);
number++;
}
}
else if (pid > 0)
{
for (int x = 0; x < 10; x++)
{
cout << "父 getpid=" << getpid() << " x=" << x << endl;
sleep(1);
}
}
return 0;
}
这就是一个僵尸状态,这个状态的子进程比父进程逻辑结束更早,进程没有逻辑可执行,子进程僵尸状态会占着内存,并不会清空。
如果当父进程结束得比子进程早,子进程就会成为孤儿进程:孤儿进程会被 init 进程(在 Linux 系统中,进程 ID 为 1 的进程)或 systemd 进程收养,由它们来负责回收孤儿进程的资源。但后续逻辑出现了问题,则不会有后续处理
当然这两个我们都不希望它们出现。
我们先来学习一个wait函数(阻塞式函数)
功能概述
在多进程编程中,父进程创建子进程后,有时需要等待子进程执行完毕,获取子进程的退出状态,以确保资源的正确回收和程序逻辑的正确执行。wait
函数就提供了这样的机制,它可以让父进程暂停执行,直到它的某个子进程终止,然后获取该子进程的终止状态信息。
头文件
#include <sys/types.h>
#include <sys/wait.h>
函数原型
pid_t wait(int *status);
- 参数:
status
是一个整型指针,用于存储子进程的退出状态信息。如果不需要获取子进程的退出状态,可以将其设置为NULL
。 - 返回值:如果调用成功,返回终止子进程的进程 ID;如果出错,返回 -1,并设置相应的错误码。
返回状态检测
宏定义 | 描述 |
---|---|
WIFEXITED(stat_val) | 子进程正常结束返回一个非0值 |
WEXITSTATUS(stat_val) | 如果WIFEXITED非0,返回子进程退出码 |
WIFSIGNALED(stat_val) | 子进程未捕获信号中止返回非0值 |
WTERMSIG(stat_val) | 如果WIFSIGNALED非0,返回信号代码 |
WIFSTOPPED(stat_val) | 如果子进程终止返回一个非0值 |
WSTOPSIG(stat_val) | 如果WIFSTOPPED非0,返回一个信号代码 |
我们对代码进行以下修改
#include <iostream>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
using namespace std;
// main函数启动,程序主进程
int main()
{
int number = 100;
// fork作用:创建新的子进程
// 一次调用 ,两次返回
pid_t pid = fork();
if (pid == 0)
{
for (int x = 0; x < 5; x++)
{
cout << "子 getpid=" << getpid() << " x=" << x << endl;
sleep(1);
number++;
}
exit(0);
}
else if (pid > 0)
{
for (int x = 0; x < 10; x++)
{
int status;
wait(&status);
cout << "父 getpid=" << getpid() << " x=" << x << endl;
sleep(1);
}
}
// 两个进程的公共代码(都会执行)
cout << "getpid=" << getpid() << "over" << endl;
return 0;
}
看结果我们发现,父进程一直等待孩子进程结束才开始执行
处理方案:
一、僵尸状态解决:
子进程在逻辑结束时将状态返还给父进程,就不会出现僵尸状态
例如以下代码,给子进程加入一个exit(2),给父进程加入一个wait(&status)函数
#include <iostream>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
using namespace std;
// main函数启动,程序主进程
int main()
{
/*pid_t pid = getpid();
while (1)
{
cout << "I am a process with PID: " << pid << endl;
sleep(1);
}*/
int number = 100;
// fork作用:创建新的子进程
// 一次调用 ,两次返回
pid_t pid = fork();
if (pid == 0)
{
for (int x = 0; x < 5; x++)
{
cout << "子 getpid=" << getpid() << " x=" << x << endl;
sleep(1);
number++;
}
exit(2);
}
else if (pid > 0)
{
for (int x = 0; x < 10; x++)
{
int status;
wait(&status);
if (WIFEXITED(status) != 0)
{
cout << "status=" << WEXITSTATUS(status) << endl;
}
cout << "父 getpid=" << getpid() << " x=" << x << endl;
sleep(1);
}
}
return 0;
}
运行结果:
二、孤儿进程解决 :
利用wait()这个函数阻塞特性,在父进程逻辑做完的最后来等待子进程结束,使父进程结束的比子进程晚。
总结:
一个基本框架
pid=fork();
if(pid==0)
{
......
exit(0);
}
else if(pid>0)
{
// wait在逻辑前还是后要根据业务需求设置
wait();
......
wait();
}