目录
一、子进程会给父进程发送SIGCHLD信号
二、父进程无需手动在main函数中等待子进程
三、问题1:多个子进程同时结束
四、问题2: 某个子进程永不退出
五、不等待子进程退出
一、子进程会给父进程发送SIGCHLD信号
子进程退出时并非悄无声息,而是会给父进程发送退出信号,即SIGCHLD。但是父进程对于该信号并不会作出处理,因为SIGCHLD信号的默认处理方式是Ign,即忽略。
父进程可以对SIGCHLD信号进行自定义捕捉,验证父进程收到了该信号:
#include <iostream>
#include <signal.h>
using namespace std;
void handler(int signum)
{cout<<"get a sig: "<<signum<<endl;
}
int main()
{signal(SIGCHLD,handler);//父进程对SIGCHLD信号自定义捕捉pid_t id=fork();//创建子进程if(id==0)//子进程{cout<<"I am child process, pid is: "<<getpid()<<endl;sleep(3);exit(1);}//父进程sleep(100);
}
二、父进程无需手动在main函数中等待子进程
根据上述特点,父进程就无需手动在main函数中使用waitpid函数进行非阻塞等待,而是在自定义函数中进行等待,父进程可以做其他任务。因为如果父进程调用了自定义函数,说明收到了SIGCHLD信号,此时子进程已经结束。
#include <iostream>
#include <signal.h>
#include <sys/wait.h>
using namespace std;
void DoOtherThing()
{cout<<"do other things"<<endl;
}
void handler(int signum)
{cout<<"get a sig: "<<signum<<endl;pid_t rid=waitpid(-1,nullptr,0);//第一个参数传-1可以等待任何进程if(rid>0){cout<<"wait child success, rid is: "<<rid<<endl;}
}
int main()
{signal(SIGCHLD,handler);//父进程对SIGCHLD信号自定义捕捉pid_t id=fork();//创建子进程if(id==0)//子进程{cout<<"I am child process, pid is: "<<getpid()<<endl;sleep(3);exit(1);}//父进程while(true){DoOtherThing();sleep(1);}
}
三、问题1:多个子进程同时结束
如果有多个子进程同时退出,同时向父进程发送SIGCHLD信号,但是父进程一次只能处理一个SIGCHLD信号,会将其他的SIGCHLD屏蔽,这样就会导致无法获取所有子进程的退出信息。
因此需要优化handler自定义函数:将进程等待设置为循环,直到所有子进程结束等待失败,结束循环(当所有子进程都结束时,即没有子进程时waitpid函数会等待失败)
#include <iostream>
#include <signal.h>
#include <sys/wait.h>
using namespace std;
void DoOtherThing()
{cout << "do other things" << endl;
}
void handler(int signum)
{//cout << "get a sig: " << signum << endl;while (true){pid_t rid = waitpid(-1, nullptr, 0); // 第一个参数传-1可以等待任何进程if (rid > 0){cout << "get a sig: " << signum << ", wait child success, rid is: " << rid << endl;}else // 等待失败,说明没有子进程可以等待了,子进程全部等待成功了{break;}}
}
int main()
{signal(SIGCHLD, handler); // 父进程对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);}
}
四、问题2: 某个子进程永不退出
假设存在多个子进程,其中某个进程永远不会退出,那么程序将永远死循环在handler函数中,main函数就永远不会结束。
因此需要进一步优化handler函数:将waitpid函数改为非阻塞等待,传入WNOHANG选项
#include <iostream>
#include <signal.h>
#include <sys/wait.h>
using namespace std;
void DoOtherThing()
{cout << "do other things" << endl;
}
void handler(int signum)
{//cout << "get a sig: " << signum << endl;while (true){pid_t rid = waitpid(-1, nullptr, WNOHANG); // 第一个参数传-1可以等待任何进程if (rid > 0){cout << "get a sig: " << signum << ", wait child success, rid is: " << rid << endl;}else if(rid < 0)// 等待失败,说明没有子进程可以等待了,子进程全部等待成功了{cout<<"wait child sucess done"<<endl;break;}else//没有收到SIGCHLD信号,但是还有子进程没退出{cout<<"wait child sucess done"<<endl;break;}}
}
int main()
{signal(SIGCHLD, handler); // 父进程对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);}
}
五、不等待子进程退出
父进程等待子进程退出主要有两个目的:1.避免子进程成为僵尸进程,释放子进程资源;2.获取子进程退出信息
如果不想等待子进程退出,可以让父进程调用signal函数,将SIGCHLD信号的默认处理方式设置为SIG_IGN,这样fork出来的子进程在结束时会被自动清理掉,不会产生僵尸进程,也不会给父进程发送任何信号或者退出信息
(注意:SIG_CHLD信号的默认处理方式本身就是Ign,但是该忽略动作是系统用的,和我们手动指定的SIG_IGN不同,只有是SIG_IGN才会使子进程退出时直接释放资源,不给父进程发送任何信息)
#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);}
}