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

  1.分配标识符pid和PCB。

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

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

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

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

  一.僵尸进程

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

  看下列代码:

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

  

  以下为运行现象:

  

  

  以及进程状态:

  

  

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

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

  二.孤儿进程

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

  看下列代码:

  

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

  

  以下为运行结果:

  

  

  以下为运行状态:

  

  

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

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

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

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

  方法1——进程等待:

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

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

  

  以下为运行结果:

  

  

  以下为进程状态:

  

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

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

  

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

使用信号方式:

  1. #include <unistd.h>
  2. #include <sys/types.h>
  3. #include <sys/wait.h>
  4. #include <signal.h>
  5. #include <errno.h>
  6. #include <stdio.h>
  7.  
  8. void sig_chld(int signo)
  9. {
  10. pid_t pid;
  11. int stat;
  12. while ( (pid = waitpid(-, &stat, WNOHANG)) > ) //while循环是表示处理所有待处理的信号,-1表示任意子进程,WNOHANG表示非阻塞
  13. printf("child %d terminated\n", pid);
  14. return;
  15. }
  16.  
  17. int main()
  18. {
  19. signal(SIGCHLD, sig_chld);
  20. pid_t cli = fork();
  21.  
  22. if (cli > ) {
  23. printf("I am father\n");
  24. } else {
  25. printf("I am child\n");
  26. _exit();
  27. }
  28. while () {
  29. sleep();
  30. }
  31. }

  以下是运行结果:

  

  以下是进程状态:

  

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

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

Zombie进程的更多相关文章

  1. Linux 的僵尸(zombie)进程

    可能很少有人意识到,在一个进程调用了exit之后,该进程 并非马上就消失掉,而是留下一个称为僵尸进程(Zombie)的数据结构.在Linux进程的5种状态中,僵尸进程是非常特殊的一种,它已经放弃了几乎 ...

  2. [转载]了解Linux的进程与线程

    本文转自Tim Yang的博客http://timyang.net/linux/linux-process/ .对于理解Linux的进程与线程非常有帮助.支持原创.尊重原创,分享知识! 上周碰到部署在 ...

  3. 僵尸进程的产生和避免,如何kill杀掉linux系统中的僵尸defunct进程

    在 Unix系统管理中,当用ps命令观察进程的执行状态时,经常看到某些进程的状态栏为defunct,这就是所谓的"僵尸"进程."僵尸"进程是一个早已 死亡的进程 ...

  4. Linux 线程与进程,以及通信

    http://blog.chinaunix.net/uid-25324849-id-3110075.html 部分转自:http://blog.chinaunix.net/uid-20620288-i ...

  5. Linux下查看进程和线程

    在linux中查看线程数的三种方法 1.top -H 手册中说:-H : Threads toggle 加上这个选项启动top,top一行显示一个线程.否则,它一行显示一个进程. 2.ps xH 手册 ...

  6. linux 僵死进程

    僵死进程简而言之就是:子进程退出时,父进程并未对其发出的SIGCHILD信号进行适当处理,导致子进程停留在僵死状态等待其父进程为其收尸,这个状态下的子进程就是僵死进程. 在fork()/execve( ...

  7. wait、waitpid 僵尸进程 孤儿进程

    man wait: NAME wait, waitpid, waitid - wait for process to change state SYNOPSIS #include <sys/ty ...

  8. Linux下的进程管理

    在操作系统系统中,进程是一个非常重要的概念. 一.Linux中进程的相关知识 1.什么是进程呢? 通俗的来说进程是运行起来的程序.唯一标示进程的是进程描述符(PID),在linux内核中是通过task ...

  9. <转载>僵尸进程

    转载http://www.cnblogs.com/scrat/archive/2012/06/25/2560904.html 什么是僵尸进程 僵尸进程是指它的父进程已经退出(父进程没有等待(调用wai ...

随机推荐

  1. data augmentation 总结

    data augmentation 几种方法总结 在深度学习中,有的时候训练集不够多,或者某一类数据较少,或者为了防止过拟合,让模型更加鲁棒性,data augmentation是一个不错的选择. 常 ...

  2. JQuery -- Jquery 中的Ajax, Jquery解析xml文件

    1. JQuery 对 Ajax 操作进行了封装,在 jQuery 中最底层的方法时 $.ajax(), 第二层是 load(), $.get() 和 $.post(),第三层是$.getScript ...

  3. windows查看端口占用、结束进程

    在开发中难免会遇到windows的端口被占用,现在我们来查看端口的占用和结束占用端口的进程. win+r 输入cmd进入命令提示符: 比如我们要查看8080端口的占用情况,输入netstat -aon ...

  4. 二路归并排序,利用递归,时间复杂度o(nlgn)

    public class MergeSort { public void mergeSort(int[]data, int left, int right) { if(left >= right ...

  5. Pandas迭代

    Pandas对象之间的基本迭代的行为取决于类型.当迭代一个系列时,它被视为数组式,基本迭代产生这些值.其他数据结构,如:DataFrame和Panel,遵循类似惯例迭代对象的键. 简而言之,基本迭代( ...

  6. NumPy使用 Matplotlib 绘制直方图

    NumPy - 使用 Matplotlib 绘制直方图 NumPy 有一个numpy.histogram()函数,它是数据的频率分布的图形表示. 水平尺寸相等的矩形对应于类间隔,称为bin,变量hei ...

  7. DPDK在OpenStack中的实现

    随着云计算与大数据的快速发展,其对数据中心网络的性能和管理提出了更高的要求,但传统云计算架构存在多个I/O瓶颈,由于云平台基本上是采用传统的X86服务器加上虚拟化方式组建,随着40G.100G高速网卡 ...

  8. LeetCode第[69]题(Java):Sqrt(x)

    题目:求平方根 难度:Easy 题目内容: Compute and return the square root of x, where x is guaranteed to be a non-neg ...

  9. Ajax基础(五)--封装库

    jQuery ajax请求的基本语法: 一.封装为对象: ajax.txt代码: {"id":"102","username":" ...

  10. linux中的权限管理命令

    一. 改变文件或目录的权限:chmod 命令详解 命令名称:chmod 命令所在路径:/bin/chmod 执行权限:所有用户 语法:chmod [{ugoa}{+-=}{rwx}] [文件或目录] ...