在测试基于 DirectFB+Gstreamer 的视频联播系统的一个 Demo 的时候,其中大量使用 system 调用的语句,例如在 menu 代码中的 system("./play") ,而且多次执行,这种情况下,在 ps -ef 列表中出现了大量的 defunct 进程,对程序的运行时有害的。按说system的源码中应该已经包含了wait,但也不能排除开发板上这个版本的system中可能没有wait,总之,开发板上在调用system后添加wait之后,defunct进程不复存在了。
下面谈谈 defunct 进程,中文翻译叫僵尸进程。下文整理于网络以及APUE一书。
一、什么是僵尸进程
在UNIX 系统中,一个进程结束了,但是他的父进程没有等待(调用wait / waitpid)他,那么他将变成一个僵尸进程。当用ps命令观察进程的执行状态时,看到这些进程的状态栏为defunct。僵尸进程是一个早已死亡的进程,但在进程表(processs table)中仍占了一个位置(slot)。
但是如果该进程的父进程已经先结束了,那么该进程就不会变成僵尸进程。因为每个进程结束的时候,系统都会扫描当前系统中所运行的所有进程,看看有没有哪个进程是刚刚结束的这个进程的子进程,如果是的话,就由Init进程来接管他,成为他的父进程,从而保证每个进程都会有一个父进程。而Init进程会自动wait其子进程,因此被Init接管的所有进程都不会变成僵尸进程。
二、UNIX下进程的运作方式
每个Unix进程在进程表里都有一个进入点(entry),核心进程执行该进程时使用到的一切信息都存储在进入点。当用 ps 命令察看系统中的进程信息时,看到的就是进程表中的相关数据。当以fork()系统调用建立一个新的进程后,核心进程就会在进程表中给这个新进程分配一个进入点,然后将相关信息存储在该进入点所对应的进程表内。这些信息中有一项是其父进程的识别码。
子进程的结束和父进程的运行是一个异步过程,即父进程永远无法预测子进程到底什么时候结束。那么会不会因为父进程太忙来不及 wait 子进程,或者说不知道子进程什么时候结束,而丢失子进程结束时的状态信息呢?
不会。因为UNIX提供了一种机制可以保证,只要父进程想知道子进程结束时的状态信息,就可以得到。这种机制就是:当子进程走完了自己的生命周期后,它会执行exit()系统调用,内核释放该进程所有的资源,包括打开的文件,占用的内存等。但是仍然为其保留一定的信息(包括进程号the process ID,退出码exit code,退出状态the terminationstatus of the process,运行时间the amount of CPU time taken by the process等),这些数据会一直保留到系统将它传递给它的父进程为止,直到父进程通过wait / waitpid来取时才释放。
也就是说,当一个进程死亡时,它并不是完全的消失了。进程终止,它不再运行,但是还有一些残留的数据等待父进程收回。当父进程 fork() 一个子进程后,它必须用 wait() (或者 waitpid())等待子进程退出。正是这个 wait() 动作来让子进程的残留数据消失。
三、僵尸进程的危害
如果父进程不调用wait / waitpid的话,那么保留的那段信息就不会释放,其进程号就会一直被占用,但是系统的进程表容量是有限的,所能使用的进程号也是有限的,如果大量的产生僵尸进程,将因为没有可用的进程号而导致系统不能产生新的进程。
所以,defunct进程不仅占用系统的内存资源,影响系统的性能,而且如果其数目太多,还会导致系统瘫痪。而且,由于调度程序无法选中Defunct 进程,所以不能用kill命令删除Defunct 进程,惟一的方法只有重启系统。
四、僵尸进程的产生
如果子进程死亡时父进程没有 wait(),通常用 ps 可以看到它被显示为“<defunct>”,这样就产生了僵尸进程。它将永远保持这样直到父进程 wait()。
由此可见,defunct进程的出现时间是在子进程终止后,但是父进程尚未读取这些数据之前。利用这一点我们可以用下面的程序建立一个defunct 进程:
#include <stdio.h>
#include<sys/types.h>
main()
{
if(!fork())
{
printf(“child pid=%d\n”, getpid());
exit(0);
}
sleep(20);
printf(“parent pid=%d \n”, getpid());
exit(0);
}
当上述程序以后台的方式执行时,第17行强迫程序睡眠20秒,让用户有时间输入ps -e指令,观察进程的状态,我们看到进程表中出现了defunct进程。当父进程执行终止后,再用ps -e命令观察时,我们会发现defunct进程也随之消失。这是因为父进程终止后,init 进程会接管父进程留下的这些“孤儿进程”(orphan process),而这些“孤儿进程”执行完后,它在进程表中的进入点将被删除。如果一个程序设计上有缺陷,就可能导致某个进程的父进程一直处于睡眠状态或是陷入死循环,父进程没有wait子进程,也没有终止以使Init接管,该子进程执行结束后就变成了defunct进程,这个defunct 进程可能会一直留在系统中直到系统重新启动。
在看一个产生僵尸进程的例子。
子进程要执行的程序test_prog
//test.c
#include <stdio.h>
int main()
{
int i = 0;
for (i = 0 ; i < 10; i++)
{
printf ("child time %d\n", i+1);
sleep (1);
}
return 0;
}
父进程father的代码father.c
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
int pid = fork ();
if (pid == 0)
{
system ("./test_prog");
_exit (0);
}else
{
int i = 0;
/*
int status = 0;
while (!waitpid(pid, &status, WNOHANG))
{
printf ("father waiting%d\n", ++i);
sleep (1);
}*/
while (1)
{
printf ("father waiting over%d\n", ++i);
sleep (1);
}
return 0;
}
}
执行./father,当子进程退出后,由于父进程没有对它的退出进行关注,会出现僵尸进程
20786 pts/0 00:00:00 father
20787 pts/0 00:00:00 father <defunct>
总结:子进程成为 defunct 直到父进程 wait(),除非父进程忽略了 SIGCLD 。更进一步,父进程没有 wait() 就消亡(仍假设父进程没有忽略 SIGCLD )的子进程(活动的或者 defunct)成为 init 的子进程,init 着手处理它们。
五、如何避免僵尸进程
1、父进程通过wait和waitpid等函数等待子进程结束,这会导致父进程挂起。
在上个例子中,如果我们略作修改,在第8行sleep()系统调用前执行wait()或waitpid()系统调用,则子进程在终止后会立即把它在进程表中的数据返回给父进程,此时系统会立即删除该进入点。在这种情形下就不会产生defunct进程。
2. 如果父进程很忙,那么可以用signal函数为SIGCHLD安装handler。在子进程结束后,父进程会收到该信号,可以在handler中调用wait回收。
3. 如果父进程不关心子进程什么时候结束,那么可以用signal(SIGCLD, SIG_IGN)或signal(SIGCHLD, SIG_IGN)通知内核,自己对子进程的结束不感兴趣,那么子进程结束后,内核会回收,并不再给父进程发送信号
4. fork两次,父进程fork一个子进程,然后继续工作,子进程fork一个孙进程后退出,那么孙进程被init接管,孙进程结束后,init会回收。不过子进程的回收还要自己做。 下面就是Stevens给的采用两次folk避免僵尸进程的示例:
#include "apue.h"
#include <sys/wait.h>
int
main(void)
...{
pid_t pid;
if ((pid = fork()) < 0) ...{
err_sys("fork error");
} else if (pid == 0) ...{ /**//* first child */
if ((pid = fork()) < 0)
err_sys("fork error");
else if (pid > 0)
exit(0); /**//* parent from second fork == first child */
/**//*
* We're the second child; our parent becomes init as soon
* as our real parent calls exit() in the statement above.
* Here's where we'd continue executing, knowing that when
* we're done, init will reap our status.
*/
sleep(2);
printf("second child, parent pid = %d ", getppid());
exit(0);
}
if (waitpid(pid, NULL, 0) != pid) /**//* wait for first child */
err_sys("waitpid error");
/**//*
* We're the parent (the original process); we continue executing,
* knowing that we're not the parent of the second child.
*/
exit(0);
}
相关推荐
参考文章如何杀死defunct进程(译)首先是通过下面的命令找到父进程的PID输出结果为:其中:如果你使用命令 “kill -9 40428” 尝试杀死 ID
在 unix 或 unix-like 的系统中,当一个子进程退出后,它就会变成一个僵尸进程,如果父进程没有通过 wait 系统调用来读取这个子进程的退出状态的话,这个子进程就会一直维持僵尸进程状态。 Zombie process – ...
DEFUNCT
$ npm install defunct 示例用法 var d = require ( 'defunct' ) ; it ( 'should be able to multiply' , function ( t ) { var mul = d . mul ( 10 ) ; t . equal ( mul ( 5 ) , 50 , 'multiply' ) t . end ( )...
Pubpatterns API API已关闭。 不再维护。
[已停产] 合成化学家的研究工具 - 档案、演示、分子建模、从头计算。
已失效
TallyMan在2000年被废弃。TallyMan是功能齐全的,高度可定制的商业和站点管理系统。 100%纯Perl / Embperl。
该项目已将名称更改为“ Surfraw”,请参见http://surfraw.sourceforge.net。
模板标量并行多模式推荐该推荐器旨在考虑广泛的用户行为,项目内容和上下文信息,以提供实时推荐。... 它还可以几种方式混合基于内容的建议,以增强协作过滤并考虑重要的上下文。 它作为模板提供,以方便安装和应用程序...
土拨鼠基于 Theano 的神经网络框架。 Marmot 旨在使实现自定义架构变得非常容易。 它针对 GPU 上的快速训练进行了优化。 特征: 前馈和循环网络具有各种学习率策略的 SGD 目前只修复了 LR 和 Adadelta,但很容易添加...
引擎 Engine 是驱动 Go web 框架的核心,具有路由、上下文、http 状态等,以弥合 Go 标准库和您自己的 web 框架之间的距离。
船队 Go 中的基本且可扩展的 Web 框架。
弃用通知 DevPost开发已被取消,因为我已经意识到许多可以达到其目的的更好的工具。... 为什么DevPost被取消? ... 它立即出现,不需要任何配置即可工作,并且在网站上工作时可以轻松地与Git-Flow集成。...
该仓库现已关闭我们已决定合并到Dat增强提案( )流程中。Dat协议Web规格Web浏览器中Dat协议的规范。 状态:刚刚开始! 此存储库中的规范仍在进行中。动机现在有多种浏览器计划实施Dat协议:因此,此存储库将: 记录...
provide open source cell phones, most of them largely defunct, ranging from the Openmoko Neo FreeRunner to QT Embedded, Moblin, LiMo, Debian Mobile, Maemo, Firefox OS, and Ubuntu Mobile to the open ...
This is the same repository as the now-defunct official GitHub Android app. What's going on here? > What happened to the old app? GitHub didn't want to maintain the app anymore, so it's been released ...
user interface for the now defunct Geofox place check-in and recommendation service. It was developed for Android 2.2 (SDK 8). Legal Copyright (C) 2011, Matt Colf, Rusty Dekema, Mike Billau, Mike ...
OLD DEFUNCT开放上下文的源代码( ) 该项目在这里进行了详细讨论: : 该项目正在Swift发展。 从2014年开始,我们开始进行重大的重构,并使用Python和Django彻底放弃了旧代码,从而完全重新开始(请参阅: : )。...