博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Zombie进程
阅读量:5889 次
发布时间:2019-06-19

本文共 4775 字,大约阅读时间需要 15 分钟。

  fork()的作用就是创建一个该进程下的子进程,在其exit 或 exec之前,和他共享代码,以一个父进程的身份,完成以下工作:

  1.分配标识符pid和PCB。

  2.让子进程复制父进程的环境。

  3.给子进程分配地址空间和资源。

  4.复制父进程的地址空间信息

 

  有了子进程,所以才有了僵尸进程和孤儿进程——

  一.僵尸进程

  创建子进程后,如果子进程比父进程早结束,而且父进程迟迟没有结束,那么子进程就会进入一个Z状态——僵尸状态,此时如果父进程不去处理,那么子进程就会一直处于这个状态,它毫无作用,又占了内存,因为其PCB中还保留了很多关于它的退出信息,所以它的PCB也不会被摧毁,这就对操作系统造成了负面影响:

  看下列代码:

1 #include
2 #include
3 #include
4 5 6 #define ERR_EXIT(m) \ 7 do \ 8 { \ 9 perror(m); \10 exit(EXIT_FAILURE); \11 } while(0)12 13 int main()14 {15 pid_t id;16 17 if((id = fork()) == -1)18 ERR_EXIT("fork");19 else if (id == 0)20 {21 printf("I am the kid,my pid : %d,my father's pid : %d!\n",getpid(),getppid());22 }23 else24 {25 while(1)26 {27 printf("I am the father,my pid : %d!\n",getpid());28 sleep(2);29 }30 }31 32 return 0;33 }

  

  以下为运行现象:

  

  

  以及进程状态:

  

  

  可以明显看到,子进程早早结束而父进程陷入死循环——子进程进入一个<defunct>状态,也就是僵尸状态,切记!僵尸状态对操作系统是有害的。所以要避免(文末最后讲两个处理方式)。

  这个状态,连最无情的kill -9 也无法处理,只能等父进程来处理。

 

 

  二.孤儿进程

  孤儿进程,顾名思义,子进程还在世的时候父进程却结束了,要记住:孤儿进程是无害的!那么孤儿进程没了父进程,是不是就被孤立了呢?不会的,我们还需要了解到1号进程——init进程,它不是第一个进程,但是是用户端的第一个进程,它在用户机开启时开始工作,在用户机结束时终止。它有一个功能就是收养这些孤儿,在这些孤儿进程结束时第一时间回收他们的退出信息,保证他们不一直成为僵尸进程。所以init进程,也被称作为孤儿院。2333333333

  看下列代码:

   

1 #include
2 #include
3 #include
4 5 6 #define ERR_EXIT(m) \ 7 do \ 8 { \ 9 perror(m); \10 exit(EXIT_FAILURE); \11 } while(0)12 13 int main()14 {15 pid_t id;16 17 if((id = fork()) == -1)18 ERR_EXIT("fork");19 else if (id == 0)20 {21 while(1)22 {23 printf("I am the kid,my pid : %d,my father's pid : %d!\n",getpid(),getppid());24 sleep(2);25 }26 }27 else28 {29 sleep(1);30 printf("I am the father,my pid : %d!\n",getpid());31 }32 33 return 0;34 }

  

  以下为运行结果:

  

  

  以下为运行状态:

  

  

  可以明显看到,在父进程未结束之前,子进程的父进程还是父进程的pid,当父进程结束后,子进程的父进程就成了init——1号进程,而且可以看到父进程是完全退出的。

  ps:此时如果你用ctrl+c,是无法结束子进程的,因为他的终端已经成了1号进程,必须找到其进程号,利用“kill -15 进程号”,来结束。

 

  三.两个处理僵尸进程的方法:

  我们试想下列这种情况:父进程是一个死循环,他会定时创造一个子进程去做一个简单的任务,但是子进程任务结束时发现父进程还在工作,所以它就处于僵尸状态等待父进程回收,那么这样的情况下,僵尸进程会越来越多,父进程只创建却不回收,迟早有一天会造成大麻烦。

 

  方法1——进程等待:

  让父进程等待子进程,子进程工作完父进程再执行工作:  

1 #include
2 #include
3 #include
4 #include
5 #include
6 7 8 #define ERR_EXIT(m) \ 9 do \10 { \11 perror(m); \12 exit(EXIT_FAILURE); \13 } while(0)14 15 int main()16 {17 pid_t id;18 19 if((id = fork()) == -1)20 ERR_EXIT("fork");21 else if (id == 0)22 {23 printf("I am the kid,my pid : %d,my father's pid : %d!\n",getpid(),getppid());24 }25 else26 {27 wait(NULL);28 while(1)29 {30 printf("I am the father,my pid : %d!\n",getpid());31 sleep(2);32 }33 }34 35 return 0;36 }

 

   

  以下为运行结果:

  

  

  以下为进程状态:

  

 

  方法2——托付给Init进程:

  这个方法是用子进程再创建一个子进程,此时子进程就成了 子进程的子进程 的父进程,然后让子进程结束,那么 子进程的子进程 接受本应该子进程接受的任务,而且 子进程的子进程 此时成了孤儿进程,他的生死父进程也不会过问,交给1号进程init来解决。

  

1 #include
2 #include
3 #include
4 #include
5 #include
6 7 8 #define ERR_EXIT(m) \ 9 do \10 { \11 perror(m); \12 exit(EXIT_FAILURE); \13 } while(0)14 15 int main()16 {17 pid_t id;18 19 if((id = fork()) == -1)20 ERR_EXIT("fork");21 else if (id == 0)22 {23 printf("I am the kid,my pid : %d,my father's pid : %d!\n",getpid(),getppid());24 }25 else26 {27 wait(NULL);28 while(1)29 {30 printf("I am the father,my pid : %d!\n",getpid());31 sleep(2);32 }33 }34 35 return 0;36 }

使用信号方式:

#include 
#include
#include
#include
#include
#include
void sig_chld(int signo){ pid_t pid; int stat; while ( (pid = waitpid(-1, &stat, WNOHANG)) > 0) //while循环是表示处理所有待处理的信号,-1表示任意子进程,WNOHANG表示非阻塞 printf("child %d terminated\n", pid); return;}int main(){ signal(SIGCHLD, sig_chld); pid_t cli = fork(); if (cli > 0) { printf("I am father\n"); } else { printf("I am child\n"); _exit(0); } while (1) { sleep(1); }}

 

  以下是运行结果:

  

 

  以下是进程状态:

  

 

  这个方法适用于子进程执行的任务在规定时间内完成但是规定时间较长,父进程不可能永久等待,所以不如交给 子进程的子进程 来处理,这样父进程就不用在乎其任务结束与否。

  当然也可以用waitpid并在第三个参数中选择非阻塞的等待方式(时间片轮转等待)。

转载地址:http://uzfsx.baihongyu.com/

你可能感兴趣的文章
C语言学习笔记--选择排序,插入排序
查看>>
异常及File类概述
查看>>
python字符编码
查看>>
import android.support.v7.app.ActionBarActivity; 报
查看>>
ImageView显示超大图片
查看>>
bash的工作特性之命令执行返回值和命令展开的内容及练习示例
查看>>
linux 命令 —— gzip
查看>>
IDEA
查看>>
SecureCRT中用vbs脚本批量执行unix命令
查看>>
.NET中的泛型和Java泛型中的类型擦除
查看>>
时间:2014年4月13日14:32:08 加入购物车
查看>>
数据存储的四种常见方式
查看>>
WebStorm常用设置和常用快捷键
查看>>
Eclipse设置软tab(用4个空格字符代替)
查看>>
oracle创建监听Oracle Net Services 配置失败。退出代码是1
查看>>
01 VMware Workstation 12 + Red Hat Enterprise Linux 7.2-x86-64-bit 安装
查看>>
IOS7原生API进行二维码条形码的扫描
查看>>
Html5添加实用的多层图片视觉差互动特效插件教程
查看>>
在linux上布置JDK
查看>>
配置nagios遇到的错误
查看>>