热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Linuxc僵尸进程的产生和清除

linuxc僵尸进程的产生和清除什么是僵尸进程僵尸进程是指它的父进程已经退出(父进程没有等待(调用waitwaitpid)它),而该进程dead之后没有进程接受,就成为僵尸进程
linuxc僵尸进程的产生和清除

什么是僵尸进程

  僵尸进程是指它的父进程已经退出(父进程没有等待(调用wait/waitpid)它),而该进程dead之后没有进程接受,就成为僵尸进程,也就是(zombie)进程。

僵尸进程是怎么样产生

  一个进程在调用exit命令结束自己的生命的时候,其实它并没有真正的被销毁,而是留下一个称为僵尸进程(Zombie)的数据结构(系统调用exit,它的作用是使进程退出,但也仅仅限于将一个正常的进程变成一个僵尸进程,并不能将其完全销毁)。

  在Linux进程的状态中,僵尸进程是非常特殊的一种,它已经放弃了几乎所有内存空间,没有任何可执行代码,也不能被调度,仅仅在进程列表中保留一个位置,记载该进程的退出状态等信息供其他进程收集,除此之外,僵尸进程不再占有任何内存空间。它需要它的父进程来为它收尸。

  如果他的父进程没安装SIGCHLD信号处理函数调用wait或waitpid()等待子进程结束,又没有显式忽略该信号,那么它就一直保持僵尸状态,如果这时父进程结束了,那么init进程自动会接手这个子进程,为它收尸,它还是能被清除的。

  但是如果父进程是一个循环,不会结束,那么子进程就会一直保持僵尸状态,这就是为什么系统中有时会有很多的僵尸进程。系统所能使用的进程号是有限的,如果大量的产生僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程.

如下代码,子进程先于父进程退出,在父进程执行的5s内,子进程将为僵尸:

#include
#include
#include
#include int main() {
//子进程的pid
int c_pid;
int pid; if ((pid = fork())) {
//父进程
c_pid = pid;
printf("The child process is %d\n", c_pid);
sleep(5);
exit(0);
} else {
//子进程
printf("I 'm a child.\n");
exit(0);
}
}

如何快速找出僵尸程序

打开终端并输入下面命令:

ps aux | grep Z

会列出进程表中所有僵尸进程的详细内容。

怎么干掉这些僵尸程序

用 SIGCHLD信号来杀死僵尸进程

正常情况下我们可以用 SIGKILL 信号来杀死进程,但是僵尸进程已经死了, 你不能杀死已经死掉的东西。 因此你需要输入的命令应该是:

ps -ef | grep pid 首先查看僵尸进程的父进程pid,输出结果的第三列ppid就是父进程的pid

kill -s SIGCHLD ppid

将这里的 pid 替换成父进程的进程 id,这样父进程就会删除所有以及完成并死掉的子进程了。

程序中避免僵尸进程的产生

  1. 父进程调用wait/waitpid。
  2. 父进程先于子进程退出,子进程自动托管到init进程。
  3. fork()两次,调用孙子进程。
  4. 绑定SIGCHLD信号处理函数,在信号处理函数中调用wait。
  5. 绑定SIGCHLD信号处理函数SIG_IGN。(不推荐)

父进程调用wait/waitpid

父进程调用waitpid()等函数来接收子进程退出状态。

#include
#include
#include
#include
#include int main() {
//子进程的pid
int c_pid;
int pid; if ((pid = fork())) {
//父进程
c_pid = pid;
printf("The child process is %d\n", c_pid);
//阻塞等待子进程
int status;
if ((pid = wait(&status)) != -1 && pid == c_pid) {
//成功回收子进程
printf("The child exit with %d\n", WEXITSTATUS(status));
fflush(stdin);
}
printf("Now , The child has been exit , and I will sleep.\n");
sleep(20);
exit(0);
} else {
//子进程
printf("I 'm a child.\n");
sleep(5);
exit(0);
}
}

父进程先exit,子进程托管到init进程

父进程先结束,子进程则自动托管到Init进程(pid = 1)。

#include
#include
#include
#include
#include int main() {
//子进程的pid
int c_pid;
int pid; if ((pid = fork())) {
//父进程
printf("I'm father id = %d.The child process is %d\n", getpid(), pid);
exit(0);
} else {
//子进程
printf("I 'm a child.\n");
sleep(5);
exit(0);
}
}

fork()两次,调用孙子进程

父进程一次fork()后产生一个子进程随后立即执行waitpid(子进程pid, NULL, 0)来等待子进程结束,然后子进程fork()后产生孙子进程随后立即exit(0)。这样子进程顺利终止(父进程仅仅给子进程收尸,并不需要子进程的返回值),然后父进程继续执行。这时的孙子进程由于失去了它的父进程(即是父进程的子进程),将被转交给Init进程托管。于是父进程与孙子进程无继承关系了,它们的父进程均为Init,Init进程在其子进程结束时会自动收尸,这样也就不会产生僵尸进程了。

#include
#include
#include
#include
#include int main() {
//子进程的pid
int c_pid;
int pid; if ((pid = fork())) {
//父进程
c_pid = pid;
printf("I'm father id = %d.The child process is %d\n", getpid(), c_pid);
//阻塞等待子进程
int status;
if ((pid = wait(&status)) != -1 && pid == c_pid) {
//成功回收子进程
printf("The child exit with %d\n", WEXITSTATUS(status));
fflush(stdin);
}
exit(0);
} else {
if ((pid = fork())) {
//子进程
printf("I 'm a child pid = %d. my child pid = %d. exit.\n", getpid(), pid);
exit(0);
} else {
//孙子进程
sleep(20);
printf("I 'm a grandson pid = %d. exit\n", pid);
exit(0);
}

}
}

绑定SIGCHLD信号处理函数,在信号处理函数中调用wait

阻塞等待,那么父进程就无法做其他事;

父进程退出,在创建守护进程时候会有,但是我们并不想让父进程退出;

fork()两次,孤儿进程不易由我们程序来管理;

man wait,查看NOTES章节,可以找到:子进程退出的时候,会发送SIGCHLD信号,默认的POSIX不响应

所以设置处理SIGCHLD信号的函数,可以用于异步回收fork子进程。

注意:信号产生触发信号处理函数时,会中断某些函数,例如poll、select、epoll等。

​ 详情见《unix网络编程》5.8小节,POSIX信号处理

#include
#include
#include
#include
#include void sig_chld(int num) {
pid_t pid;
int stat;
while ( (pid = waitpid(-1, &stat, WNOHANG)) > 0) // 循环处理完所有的信号
printf("child %d terminated\n", pid);
return;
} int main() {
//子进程的pid
int c_pid;
int pid; signal(SIGCHLD, sig_chld); if ((pid = fork())) {
//父进程
c_pid = pid;
printf("The child process is %d\n", c_pid); //父进程不用等待,做自己的事情吧~
for (int i = 0; i <10; i++) {
printf("Do parent things.\n");
sleep(1);
} exit(0);
} else {
//子进程
printf("I 'm a child.\n");
sleep(2);
exit(0);
}
}

绑定SIGCHLD信号处理函数SIG_IGN

不推荐使用,因为POSIX标准中未规定SIG_IGN,会破坏程序的可移植性。

#include
#include
#include
#include
#include int main() {
//子进程的pid
int c_pid;
int pid; signal(SIGCHLD,SIG_IGN); if ((pid = fork())) {
//父进程
c_pid = pid;
printf("The child process is %d\n", c_pid); //父进程不用等待,做自己的事情吧~
for (int i = 0; i <10; i++) {
printf("Do parent things.\n");
sleep(1);
} exit(0);
} else {
//子进程
printf("I 'm a child.\n");
sleep(2);
exit(0);
}
}

推荐阅读
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • 本文详细介绍了SQL日志收缩的方法,包括截断日志和删除不需要的旧日志记录。通过备份日志和使用DBCC SHRINKFILE命令可以实现日志的收缩。同时,还介绍了截断日志的原理和注意事项,包括不能截断事务日志的活动部分和MinLSN的确定方法。通过本文的方法,可以有效减小逻辑日志的大小,提高数据库的性能。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • Commit1ced2a7433ea8937a1b260ea65d708f32ca7c95eintroduceda+Clonetraitboundtom ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • 目录实现效果:实现环境实现方法一:基本思路主要代码JavaScript代码总结方法二主要代码总结方法三基本思路主要代码JavaScriptHTML总结实 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • JavaSE笔试题-接口、抽象类、多态等问题解答
    本文解答了JavaSE笔试题中关于接口、抽象类、多态等问题。包括Math类的取整数方法、接口是否可继承、抽象类是否可实现接口、抽象类是否可继承具体类、抽象类中是否可以有静态main方法等问题。同时介绍了面向对象的特征,以及Java中实现多态的机制。 ... [详细]
  • Oracle分析函数first_value()和last_value()的用法及原理
    本文介绍了Oracle分析函数first_value()和last_value()的用法和原理,以及在查询销售记录日期和部门中的应用。通过示例和解释,详细说明了first_value()和last_value()的功能和不同之处。同时,对于last_value()的结果出现不一样的情况进行了解释,并提供了理解last_value()默认统计范围的方法。该文对于使用Oracle分析函数的开发人员和数据库管理员具有参考价值。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
author-avatar
铁人萱小小
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有