我们可否想过一个问题:使用fork()函数创建子进程,因为父进程和子进程的执行顺序是随机的

当父进程已经结束了,子进程还会继续存在并正常执行吗?

我们先看这个例子:

guer1.c

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>int main()
{pid_t pid = fork(); if(pid<0)perror("fork error!");    else if (pid>0)printf("I am parent , pid is %d\n",getpid());else{sleep(2);//保证父进程优先结束printf("I am child , pid is %d\n",getpid());}  return 0;
} 

我们控制父进程优先执行,可以看到子进程还是可以执行,但是为什么把输出越界了呢,而不是正常的在两个指令行之间输出?

因为父进程结束的时候我们程序就退出了,而两个指令行之间的空间是我们上面执行的程序的空间,子进程执行的位置发生了变化,说明他已经不在属于刚才的父进程了,那是怎么肥四呢?

其实当父进程执行完毕之后,就结束了,子进程的爹没了,他就变成了一个孤儿进程,那他怎么办呢?他找到了他的 太太太太太爷爷,init进程( 在Linux系统中,当内核启动了自己之后,init是第一个用户进程,它的进程号是1,是系统中所有其他用户进程的祖先进程 ),这时候init进程会带领被父亲抛弃的孤儿进程执行完毕后退出。

我们可以看一下下面这个程序对这个问题的实践

guer2.c

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main()
{pid_t pid=fork();if(pid<0)perror("fork error!");else if(pid==0){//child printf("I am child process \n");printf("pid:%d----ppid:%d\n",getpid(),getppid());printf("I will sleep for 3 seconds\n");sleep(3);printf("pid:%d----ppid:%d\n",getpid(),getppid());printf("child process exit\n");}else{//parent printf("I am father process\n");sleep(1);//让子进程执行到sleepprintf("father process exit\n");  }return 0;
}

我们可以看到父进程结束后,子进程的ppid变成了1,成功地被init进程收养了

刚才说的是父进程优先执行完成,那么子进程优先完成的时候怎么办呢?也是直接退出吗?

答案是不是的

正常情况下,子进程是通过父进程创建的,子进程的结束和父进程的运行是一个异步过程,即父进程无法预测子进程 到底什么时候结束。

unix提供了一种机制可以保证只要父进程想知道子进程结束时的状态信息,就可以得到。

这种机制就是: 在每个进程退出的时候,内核会向父进程发送SIGCHLD信号,内核释放该进程所有的资源,包括打开的文件,占用的内存等。 但是仍然为其保留一定的信息(包括进程号,退出状态,运行时间等),保留的这些信息就是所谓的僵尸进程!

我们可以看一下僵尸的状态:

jiangshi.c

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>int main()
{pid_t pid=fork();if(pid<0)perror("fork error!");else if(pid==0){printf("I am child process,I exit\n");exit(0);}printf("I am father process\n");printf("I will sleep for 2 seconds\n");sleep(2);//等待子进程退出system("ps -o pid,ppid,state,tty,command");//输出进程信息 printf("father process exit\n");return 0;
}

Z的意思就是 Zombie,即僵尸

根据僵尸进程的定义我们可以知道,只要是创建了子进程,一定会产生僵尸进程,所以僵尸进程并不可怕,但是僵尸进程是占内存的,我们知道一般进程只能同时存在300-400个左右,假如我们写了一个非常大的程序,需要不断的创建非常多的子进程,正常情况下我们渴望的是让他们轮流存活,也就是交替的创建和销毁,但是如果我们没能及时的销毁这些僵尸进程导致累积过多的话,就会使得后面的进程创建失败而使得程序作废

那么僵尸进程是怎么被处理的呢?Linux提供了一个wait()函数,wait很明显是等待的意思,但是他和sleep()不一样,不是挂起不执行,而是等待子进程一起结束,他可以使父进程获得子进程的信息,在父进程执行完成退出之前销毁子进程产生的僵尸进程,关于wait()的机制可以看下面的这两个图,就不再详细讲了。。

其实是两个函数wait()和waitpid(),二者的作用比较类似,都是处理僵尸进程,但是调用的方法及作用也有区别,我们来看一下:

pid_t wait(int* status)

进程一旦调用了wait,就立即阻塞自己,由wait自动分析是   否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回,返回值是销毁的子进程的id;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。  参数status用来保存被收集进程退出时的一些状态,它是一个指向int类型的指针。但如果我们对这个子进程是如何死掉的毫不在意,只想把这个僵尸进程消灭掉,我们就可以设定这个参数为NULL ,想知道的话是可以百度的到的,嘻嘻

pid = wait(NULL); 

wait1.c

#include<sys/types.h>
#include<sys/wait.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main()
{pid_t pc,pr;pc=fork();if(pc<0)perror("creat process error");else if(pc==0){printf("child process,pid is %d\n",getpid());sleep(10);}else {pr=wait(NULL);printf("parent proccess,my child pid is %d\n",pr);}exit(0);
}

这样父进程就可以获得子进程的pid

pid_t waitpid(pid_t pid , int* status , int options)

返回值和参数status和wait()函数一样,

pid:

pid>0时,只等待进程ID等于pid的子进程,不管其它已经有多少子进程运行结束退出了,只要指定的子进程还没有结束,waitpid就会一直等下去。

pid==-1时,等待任何一个子进程退出,没有任何限制,此时waitpid和wait的作用一模一样。

pid==0时,等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid不会对它做任何理睬。

pid<-1时,等待一个指定进程组中的任何子进程,这个进程组的ID等于pid的绝对值。

options:

options提供了一些额外的选项来控制waitpid,目前在Linux中只支持WNOHANG和WUNTRACED两个选项,这是两个常数,如果使用了WNOHANG(wait no hung)参数调用waitpid,即使没有子进程退出,它也会立即返回,不会像wait那样永远等下去。而WUNTRACED参数,由于涉及到一些跟踪调试方面的知识,加之极少用到, 如果我们不想使用它们,也可以把options设为0

pid=waitpid(-1,NULL,0);

waitpid1.c

#include<sys/types.h>
#include<sys/wait.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main()
{pid_t pc,pr;pc=fork();if(pc<0)perror("creat process error");else if(pc==0){printf("child process,pid is %d\n",getpid());sleep(10);}else {pr=waitpid(-1,NULL,0);printf("parent proccess,my child pid is %d\n",pr);}exit(0);
}

我们使得waitpid和wait函数取得了相同的效果,赞!

Linux多进程编程之 孤儿进程僵尸进程+wait函数相关推荐

  1. Linux系统编程 74 孤儿进程和僵尸进程

    Linux系统编程  74 孤儿进程和僵尸进程 学习笔记 孤儿进程:父进程先于子进程结束,则子进程成为孤儿进程,子进程的父进程成为 init进程,称为init进程领养了孤儿进程. init进程会去接替 ...

  2. linux系统编程学习_(2)进程控制-- fork函数、exec函数族、回收子进程--孤儿进程僵尸进程、wait函数

    linux系统编程学习_(2)进程控制-- fork函数.exec函数族.回收子进程–孤儿进程僵尸进程.wait函数 进程控制 fork()函数 创建一个子进程. pid_t fork(void); ...

  3. Linux -- 多进程编程之 - 守护进程

    内容概要 一.守护进程概述 二.守护进程创建 2.1.创建子进程,父进程退出 2.2.在子进程中创建新会话 2.2.1.进程组和会话期 2.2.2.setsid()函数说明 2.3.改变当前工作目录 ...

  4. 浅析三种特殊进程:孤儿进程,僵尸进程和守护进程

    其实有时想想linux内核的设计也蕴含着很多人生哲学,在linux中有这么几个特殊进程中,我们一开始见到它们的名字可能还会觉得很诧异,但在了解完了原理后,我们仔细想想,这样的命名也不无道理!下面我就给 ...

  5. 浅析三种特殊进程:孤儿进程,僵尸进程和守护进程.

    其实有时想想linux内核的设计也蕴含着很多人生哲学,在linux中有这么几个特殊进程中,我们一开始见到它们的名字可能还会觉得很诧异,但在了解完了原理后,我们仔细想想,这样的命名也不无道理!下面我就给 ...

  6. 守护进程/僵尸进程/孤儿进程

    一 守护进程 守护进程就是在后台运行,不与任何终端关联的进程,,一个守护进程的父进程是init进程,它是一个孤儿进程,没有控制终端,所以任何输出,无论是向标准输出设备stdout还是标准出错设备std ...

  7. Linux系统编程(六)守护进程

    Linux系统编程(六)守护进程 一.进程组 概念 二.会话 创建会话的条件 守护进程 概念 守护进程模型 创建守护进程 一.进程组 概念 进程组,也称之为作业.代表一个或多个进程的集合.每个进程都属 ...

  8. Linux中的defunct进程(僵尸进程)

    一.什么是defunct进程(僵尸进程)? 在 Linux 系统中,一个进程结束了,但是他的父进程没有等待(调用wait / waitpid)他,那么他将变成一个僵尸进程.当用ps命令观察进程的执行状 ...

  9. Kill杀死Linux中的defunct进程(僵尸进程)

    一.什么是defunct进程(僵尸进程)? 在 Linux 系统中,一个进程结束了,但是他的父进程没有等待(调用wait / waitpid)他,那么他将变成一个僵尸进程.当用ps命令观察进程的执行状 ...

最新文章

  1. RMAN干掉热备份#OCP试验1#
  2. Redis在C#中的使用及Redis的封装
  3. RDF -- 资源描述框架
  4. Linux内核分析作业第二周
  5. 手把手教你代码重构,是时候告别屎一样的代码了!
  6. 深刻理解:反向代理服务器
  7. Eclipse+SprignBoot实现文件上传
  8. java计算八皇后_八皇后java算法
  9. django项目日志
  10. PHP artisan auth,Php artisan make:auth命令未定义
  11. 国产杀毒软件也开始支持虚拟化
  12. TextView跑马灯效果
  13. CA ARCserve Backup系列(1)—安装
  14. 使用python对学生表的查询_python + mysql 实现查询表数据
  15. Visual Studio2012版安装教程--C++新手初学者
  16. 发起成立“ABCD联盟”,人工智能与区块链技术研讨会北京站精彩回顾
  17. 数字图像取证:初学者手册
  18. java中yml后缀文件_YML文件扩展名 - 什么是.yml以及如何打开? - ReviverSoft
  19. Python 自动刷新网页
  20. 共模电感磁芯材质你知道哪几种

热门文章

  1. 互联网如何切入教育?
  2. 直播回顾|第12期5G消息云课堂,汽车服务的“升级密码” | 文末有回放
  3. 3D建模技术-讲稿-8-马克杯-动画
  4. ubuntu挂载usb光驱_Linux 文件系统挂载mount命令讲解
  5. 前缀、中缀、后缀表达式 快速计算方法
  6. gradle学习入门:Gradle是什么?Gradle有什么用?
  7. android smb开源,搭建samba服务---实现跨平台文件共享
  8. 数据库备份与恢复简介
  9. 怎么查看Python的源代码
  10. 曝苹果最早4月推出新款iPad Pro 配备雷电接口,与M1 Mac相同