僵尸进程与孤儿进程
概述
1. 除了pid = 0以外的所有进程,都是由其他进程调用fork创建的,也就是说所有进程都是有父进程的
2. 子进程和父进程的运行是一个异步状态,也就是父亲永远不知到儿子啥时候会死,当一个子进程完成任务死亡后,它的进程控制块PCB仍然保留在内存中,它的爸爸需要调用wait()或waitpid系统调用来得知儿子的状态
3. 儿子死后会告诉利用返回码告诉操作系统,操作系统就告知儿子的父亲自己的儿子死了,利用SIGCHLD信号,父亲会使用系统wait系统调用读取儿子的退出状态,只有父进程读取后,内核才会把儿子的PCB给释放掉(收尸+火化)
4. 如果父亲一直不调用wait或waitpid函数读取儿子的状态,那么内核就无法为儿子收尸和火化,只能等着它的父亲知道儿子的状态后才能收尸+火化,或者说父亲也死了的时候才会一同为他俩收尸和火化
5. 儿子死了,但爸爸迟迟不去调用wait或waitpid读取儿子的结束状态,导致儿子的PCB一直留在内存中,就是一直无法为儿子收尸,因为父亲还没有看儿子最后一眼,内核实在不忍心收尸火化,这个时候子进程就是僵尸进程
6. 另一种情况,就是爸爸死了儿子还没死,这个时候儿子就成了孤儿,叫做孤儿进程,那它只能被送到孤儿院(init进程,进程号为1)来收养它,当子进程结束后,init进程会循环使用wait()函数,因此子进程死掉后,内核就会回收,不会向僵尸进程那样占用这内存
查看代码
#include
#include
#include
int main(){
printf("pid = %d\n",getpid());
pid_t pid = fork();
if(pid == 0){
while(1)
{
printf("father pid = %d\n", getppid());
printf("child pid = %d\n",getpid());
sleep(1);
}
}
else if(pid > 0){
printf("This is the parent!pid = %d\n",getpid());
sleep(3);
exit(0);
}
return 0;
}
查看代码
#include
#include
#include
int main(){
printf("pid = %d\n",getpid());
pid_t pid = fork();
if(pid == 0){
printf("father pid = %d\n", getppid());
printf("child pid = %d\n",getpid());
sleep(1);
}
else if(pid > 0){
printf("This is the parent!pid = %d\n",getpid());
sleep(5);
exit(0);
}
return 0;
}
- 僵尸进程和孤儿进程有哪些危害:
- 僵尸进程会占用系统资源,过多的僵尸进程可能会影响服务器性能
- 孤儿进程没啥危害,孤儿进程还活着,被送进了孤儿院(init进程管理)
- 僵尸进程如何避免:
- 父进程调用wait或waitpid等待子进程结束
查看代码
#include
#include
#include
#include
int main(){
printf("pid = %d\n",getpid());
pid_t pid = fork();
if(pid == 0){
printf("father pid = %d\n", getppid());
printf("child pid = %d\n",getpid());
sleep(1);
exit(100);
}
else if(pid > 0){
printf("This is the parent!pid = %d\n",getpid());
sleep(5);
int status = 0;
int child_pid = wait(&status);
std::cout<<"child pid is "<
- fork两次,父进程fork出子进程,子进程fork出孙进程,然后子进程就退出,此时孙进程就变成了孤儿,它爷爷才不会管他呢,所以只能进孤儿院交给init进程,所以它退出后就不会变成僵尸进程,但是子进程还是需要父进程来回收
- 若父进程比较忙,不想调用wait或waitpid来挂起等待,那么它就可以使用SIGCHLD信号,当儿子死后,内核会告知它爹,然后父进程在信号处理函数中再调用wait或waitpid函数就可以了,这样儿子就不是僵尸进程了
查看代码
#include
#include
#include
#include
#include
#include
#include
#include
void process(int signo)
{
int status;
pid_t pid;
pid = waitpid(0, &status, WNOHANG);
std::cout<<"child dead pid is "< 0){
struct sigaction act;
act.sa_handler = process;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
//对SIGCHLD信号的解释:子进程结束时,父进程会收到这个信号。默认动作为忽略这个信号。
//sigaction函数的作用是修改信号处理动作,不让执行默认动作,去执行sigaction函数注册的回调函数
sigaction(SIGCHLD, &act, NULL);
while(1){
printf("parent ID %d\n",getpid());
sleep(1);
}
return 0;
}
}
3. 在父进程中使用signal(SIGCHLD, SIGIGN)函数,告诉内核,他不关心这儿子的死活,死了就直接收尸埋了算了,所以儿子死后,内核会释放其PCB不用等它爸再看最后一眼了
查看代码
#include
#include
#include
#include
#include
#include
#include
#include
int main()
{
pid_t pid;
pid = fork();
if(pid == 0){
std::cout<<"child pid is "< 0){
signal(SIGCHLD,SIG_IGN); //忽略SIGCHLD信号
while(1){
printf("parent ID %d\n",getpid());
sleep(1);
}
return 0;
}
}
- 僵尸进程的查看
- 查看僵尸进程:
ps -aux | grep Z
- 僵尸进程的处理
- 找到僵尸进程的父进程:
参考文献
1. 进程3.0——进程状态与僵尸进程、孤儿进程