Zombie进程
fork()的作用就是创建一个该进程下的子进程,在其exit 或 exec之前,和他共享代码,以一个父进程的身份,完成以下工作:
1.分配标识符pid和PCB。
2.让子进程复制父进程的环境。
3.给子进程分配地址空间和资源。
4.复制父进程的地址空间信息。
有了子进程,所以才有了僵尸进程和孤儿进程——
一.僵尸进程
创建子进程后,如果子进程比父进程早结束,而且父进程迟迟没有结束,那么子进程就会进入一个Z状态——僵尸状态,此时如果父进程不去处理,那么子进程就会一直处于这个状态,它毫无作用,又占了内存,因为其PCB中还保留了很多关于它的退出信息,所以它的PCB也不会被摧毁,这就对操作系统造成了负面影响:
看下列代码:
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<stdlib.h>
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 else
24 {
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<stdio.h>
2 #include<unistd.h>
3 #include<stdlib.h>
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 else
28 {
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——进程等待:
让父进程等待子进程,子进程工作完父进程再执行工作:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<sys/types.h> #define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while() int main()
{
pid_t id; if((id = fork()) == -)
ERR_EXIT("fork");
else if (id == )
{
printf("I am the kid,my pid : %d,my father's pid : %d!\n",getpid(),getppid());
}
else
{
wait(NULL);
while()
{
printf("I am the father,my pid : %d!\n",getpid());
sleep();
}
} return ;
}
以下为运行结果:
以下为进程状态:
方法2——托付给Init进程:
这个方法是用子进程再创建一个子进程,此时子进程就成了 子进程的子进程 的父进程,然后让子进程结束,那么 子进程的子进程 接受本应该子进程接受的任务,而且 子进程的子进程 此时成了孤儿进程,他的生死父进程也不会过问,交给1号进程init来解决。
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<sys/types.h> #define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while() int main()
{
pid_t id; if((id = fork()) == -)
ERR_EXIT("fork");
else if (id == )
{
printf("I am the kid,my pid : %d,my father's pid : %d!\n",getpid(),getppid());
}
else
{
wait(NULL);
while()
{
printf("I am the father,my pid : %d!\n",getpid());
sleep();
}
} return ;
}
使用信号方式:
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <errno.h>
#include <stdio.h> void sig_chld(int signo)
{
pid_t pid;
int stat;
while ( (pid = waitpid(-, &stat, WNOHANG)) > ) //while循环是表示处理所有待处理的信号,-1表示任意子进程,WNOHANG表示非阻塞
printf("child %d terminated\n", pid);
return;
} int main()
{
signal(SIGCHLD, sig_chld);
pid_t cli = fork(); if (cli > ) {
printf("I am father\n");
} else {
printf("I am child\n");
_exit();
}
while () {
sleep();
}
}
以下是运行结果:
以下是进程状态:
这个方法适用于子进程执行的任务在规定时间内完成但是规定时间较长,父进程不可能永久等待,所以不如交给 子进程的子进程 来处理,这样父进程就不用在乎其任务结束与否。
当然也可以用waitpid并在第三个参数中选择非阻塞的等待方式(时间片轮转等待)。
Zombie进程的更多相关文章
- Linux 的僵尸(zombie)进程
可能很少有人意识到,在一个进程调用了exit之后,该进程 并非马上就消失掉,而是留下一个称为僵尸进程(Zombie)的数据结构.在Linux进程的5种状态中,僵尸进程是非常特殊的一种,它已经放弃了几乎 ...
- [转载]了解Linux的进程与线程
本文转自Tim Yang的博客http://timyang.net/linux/linux-process/ .对于理解Linux的进程与线程非常有帮助.支持原创.尊重原创,分享知识! 上周碰到部署在 ...
- 僵尸进程的产生和避免,如何kill杀掉linux系统中的僵尸defunct进程
在 Unix系统管理中,当用ps命令观察进程的执行状态时,经常看到某些进程的状态栏为defunct,这就是所谓的"僵尸"进程."僵尸"进程是一个早已 死亡的进程 ...
- Linux 线程与进程,以及通信
http://blog.chinaunix.net/uid-25324849-id-3110075.html 部分转自:http://blog.chinaunix.net/uid-20620288-i ...
- Linux下查看进程和线程
在linux中查看线程数的三种方法 1.top -H 手册中说:-H : Threads toggle 加上这个选项启动top,top一行显示一个线程.否则,它一行显示一个进程. 2.ps xH 手册 ...
- linux 僵死进程
僵死进程简而言之就是:子进程退出时,父进程并未对其发出的SIGCHILD信号进行适当处理,导致子进程停留在僵死状态等待其父进程为其收尸,这个状态下的子进程就是僵死进程. 在fork()/execve( ...
- wait、waitpid 僵尸进程 孤儿进程
man wait: NAME wait, waitpid, waitid - wait for process to change state SYNOPSIS #include <sys/ty ...
- Linux下的进程管理
在操作系统系统中,进程是一个非常重要的概念. 一.Linux中进程的相关知识 1.什么是进程呢? 通俗的来说进程是运行起来的程序.唯一标示进程的是进程描述符(PID),在linux内核中是通过task ...
- <转载>僵尸进程
转载http://www.cnblogs.com/scrat/archive/2012/06/25/2560904.html 什么是僵尸进程 僵尸进程是指它的父进程已经退出(父进程没有等待(调用wai ...
随机推荐
- mysql官网下载链接——绿色版&安装版
windows64位5.5.60安装版 https://downloads.mysql.com/archives/get/file/mysql-5.5.60-winx64.msi windows64位 ...
- 抓jsoup_02_数据
1.测试网页:http://ajax.mianbao99.com/vod-showlist-id-8-order-time-c-3719-p-1.html ZC: 直接查看的话,使用这个链接:http ...
- Java 各种锁的小结
一. synchronized 在 JDK 1.6 之前,synchronized 是重量级锁,效率低下. 从 JDK 1.6 开始,synchronized 做了很多优化,如偏向锁.轻量级锁.自旋锁 ...
- iOS CoreData版本升级和数据库迁移
app中使用了CoreData,并且在下一个版本中有实体变动,比如实体新增字段.修改字段等改动, 那么app在覆盖安装时就要进行数据库迁移, 否则app就会crash. 那如何实现数据库迁移呢?大概需 ...
- 一些官方的github地址
阿里巴巴开源github地址:https://github.com/alibaba 腾讯开源github地址:https://github.com/Tencent 奇虎360github地址:http ...
- 虚拟主机(多站点配置)的实现--centos上的实现
Apache中配置多主机多站点,可以通过两种方式实现 将同一个域名的不同端口映射到不同的站点(虚拟主机) 将同一个端口映射成不同的域名,不同的域名映射到不同的站点 两种方法可以同时存在,局域网通过 ...
- python学习笔记(requests)
昨天用jmeter尝试了下接口测试 在部分接口中要上传文件这里遇到了问题.今天改用python的requests框架试下 先简单的写了个登录的接口.本人初学者,第一次写接口脚本 #!/usr/bin/ ...
- 负载均衡之HTTP重定向
转载请说明出处:http://blog.csdn.net/cywosp/article/details/38014581 由于目前现有网络的各个核心部分随着业务量的提高,访问量和数据流量的快速增长,其 ...
- Selenium with Python 009 - WebDriver API
官方API文档:https://seleniumhq.github.io/selenium/docs/api/py/api.html 更多详情,最好的学习方式可以查阅官方API文档或直接阅读源码,本文 ...
- pOJ-1061 exgcd求同余方程组
链接 就是求(m-n)*a+b*l=y-x, 类似于求解a*x+b*y=c,r=gcd(a,b),当c%r==0时有解,用exgcd求出a*x+b*y=gcd(a,b)的解,然后x*c/gcd(a,b ...