目录
父进程需要通过等待,回收子进程资源,获取子进程退出信息,否则子进程会成为僵尸进程。
一、阻塞等待
使用wait函数或者waitpid函数(option参数传0)实现阻塞等待
弊端:如果子进程一直不退出,父进程将一直处于阻塞状态
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
void ChildRun()
{
int i=0;
for(i=0;i<5;++i)
{
printf("Child process is running!\n");
}
}
int main()
{
pid_t id=fork();
//子进程
if(id==0)
{
ChildRun();
exit(0);//子进程退出
}
//父进程
int status=0;
pid_t rid=wait(&status);
if(rid>0)
{
printf("Father process wait success! status:%d, child quit code:%d, child quit signal:%d\n",status,(status>>8)&0xFF,status&0x7F);
}
else
{
printf("Father process wait fialed!\n");
}
return 0;
}
二、循环式非阻塞等待
使用waitpid函数(option参数传WNOHANG)
弊端:资源消耗大,能效低
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
void ChildRun()
{
int i=0;
for(i=0;i<5;++i)
{
printf("Child process is running!\n");
}
}
void DoOtherthing()
{
printf("Child process is not end! Father process is do other thing...\n");
}
int main()
{
pid_t id = fork();
if (id < 0)
{
printf("fork error!\n"); // 子进程创建失败
}
else if (id == 0) // 子进程执行
{
ChildRun();
exit(123);
}
else // 父进程执行
{
int status = 0;
pid_t rid = waitpid(id, &status, WNOHANG); // 非阻塞等待
// 子进程未终止
while (rid == 0)
{
DoOtherthing(); // 父进程做其他事情
rid = waitpid(id, &status, WNOHANG);
}
// 子进程等待失败
if (rid == -1)
{
printf("wait failed!\n");
}
else // 子进程等待成功
{
if (WIFEXITED(status)) // 子进程正常终止
{
printf("wait success! child exit code:%d\n", WEXITSTATUS(status));
}
else // 子进程异常终止
{
printf("wait success! child abnormal exit\n");
}
}
}
return 0;
}
三、父进程忽略SIG_CHLD信号
子进程退出时会给父进程发送17号信号:SIG_CHLD,父进程调用signal函数忽略该信号,则子进程结束后会自动释放资源,无需父进程等待
#include <iostream>
#include <signal.h>
#include <sys/wait.h>
using namespace std;
void DoOtherThing()
{
cout << "do other things" << endl;
}
int main()
{
signal(SIGCHLD, SIG_IGN); // 忽略SIGCHLD信号
for (int i = 0; i < 5; ++i)//创建5个子进程
{
pid_t id = fork(); // 创建子进程
if (id == 0) // 子进程
{
cout << "I am child process, pid is: " << getpid() << endl;
sleep(1);
exit(1);
}
}
// 父进程
while (true)
{
DoOtherThing();
sleep(1);
}
}
四、双fork策略
父进程采用阻塞等待的方式等待子进程,但是为了避免子进程不退出而导致父进程一直阻塞的问题,所以产生了双fork策略:
子进程额外创建孙子进程,让孙子进程代替子进程完成相关任务,同时子进程立即退出,父进程等待子进程成功。由于子进程的退出,孙子进程成为了孤儿进程,孤儿进程结束后会被操作系统领养,进而释放资源,无需被等待。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main() {
pid_t pid_b, pid_c;
int status;
// 父进程创建子进程B
pid_b = fork();
if (pid_b < 0) {
// fork失败
perror("fork failed");
exit(EXIT_FAILURE);
} else if (pid_b == 0) {
// 子进程B的代码
printf("This is child process B (PID: %d)\n", getpid());
// 子进程B创建孙子进程C
pid_c = fork();
if (pid_c < 0) {
// fork失败
perror("fork failed in child process B");
exit(EXIT_FAILURE);
} else if (pid_c == 0) {
// 孙子进程C的代码
printf("This is grandchild process C (PID: %d)\n", getpid());
// 孙子进程C执行其任务...
// 这里只是简单地睡眠一段时间来模拟任务执行
sleep(10);
// 孙子进程C任务完成,退出
printf("Grandchild process C exiting...\n");
exit(EXIT_SUCCESS);
} else {
// 子进程B中,孙子进程C已经创建并运行
// 子进程B现在可以安全地退出
printf("Child process B exiting after creating grandchild process C (PID: %d)\n", pid_c);
exit(EXIT_SUCCESS);
}
} else {
// 父进程的代码
printf("This is parent process (PID: %d)\n", getpid());
// 父进程等待子进程B的退出
printf("Parent process waiting for child process B (PID: %d) to exit...\n", pid_b);
waitpid(pid_b, &status, 0); // 使用waitpid等待特定的子进程B
if (WIFEXITED(status)) {
printf("Child process B exited with status %d\n", WEXITSTATUS(status));
} else {
printf("Child process B did not exit normally\n");
}
// 父进程可以选择继续执行其他任务
// 或者简单地结束
printf("Parent process exiting...\n");
}
return 0;
}