磁县网站推广,网站建设实训报告模版,浙江省建设执业注册中心网站,互联网公司设计在测试基于 DirectFBGstreamer 的视频联播系统的一个 Demo 的时候#xff0c;其中大量使用 system 调用的语句#xff0c;例如在 menu 代码中的 system(./play) #xff0c;而且多次执行#xff0c;这种情况下#xff0c;在 ps -ef 列表中出现了大量的 defunc…在测试基于 DirectFBGstreamer 的视频联播系统的一个 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 进程 C代码
#include stdio.h #includesys/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 C代码
//test.c #include stdio.h int main() { int i 0; for (i 0 ; i 10; i) { printf (child time %d\n, i1); sleep (1); } return 0; } 父进程father的代码father.c 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,当子进程退出后由于父进程没有对它的退出进行关注会出现僵尸进程 C代码
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)或signalSIGCHLD, SIG_IGN通知内核自己对子进程的结束不感兴趣那么子进程结束后内核会回收并不再给父进程发送信号 4. fork两次父进程fork一个子进程然后继续工作子进程fork一个孙进程后退出那么孙进程被init接管孙进程结束后init会回收。不过子进程的回收还要自己做。 下面就是Stevens给的采用两次folk避免僵尸进程的示例 C代码
#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 */ /**//* * Were the second child; our parent becomes init as soon * as our real parent calls exit() in the statement above. * Heres where wed continue executing, knowing that when * were 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); /**//* * Were the parent (the original process); we continue executing, * knowing that were not the parent of the second child. */ exit(0); }