您的位置:首页 > 运维架构 > Linux

Linux 系统中僵尸进程

2013-10-18 10:42 537 查看

书单

碰上国庆,加上这周,消化掉几本书:
《软件随想录》:Joel的随想录,也就是他Blog上的文章的摘选;整本书看下来轻松;写了篇读书笔记[传送门];
《中国在梁庄》:这本书描述了作者对其家乡的调研结果,中国农村,生活比以前要富足,但也比以前更让人绝望。其影响可能还要一二十年才会爆发,主要集中在两点:1、身体健康和精神状态,2、家庭问题。对中国农村现状剖析的很透彻;
《C++设计与演化》:未全读完,捡感兴趣的看看;

本周见闻

1) 户籍制度怎么改?
http://blog.sina.com.cn/s/blog_49818dcb0102fj0p.html
叶檀:发达的城市群之间各城市户籍可以互通。
发达地区城乡区别不大,是打破城乡差别的契机。这些地区可以通过建立大城市群逐步放开户籍门槛。
中国四大一线城市北上广深集中了优质资源,房价一样高,保障水平大同小异,完全可以取消四大城市之间的户口限制。现在各城市之间堡垒固若金汤,一位知名的社会学家从北京到广州工作,错过了一个月的选择期,户口再也无法从北京迁至广州。北京与广州户口如同天堑,滑天下之大稽。

2) 10个提高网站可用性的经验指南
http://lusongsong.com/info/post/574.html
行高(px)÷ 英文字符大(px)=1.48
1.5通常被认为是最经典的排字方式,我们的研究决心为这个方式点个赞。几乎没有网站使用低于它的,那些高于这个标准的也会越来越少,特别是当你看完这篇文章后。
PS:在博客中使用字体设置后,字里行间就多了些内容;这样,当别人再订阅的时候,RSS阅读器里的字体就很固定,并且无法调整;
看来得抛弃我喜欢的华文中宋,还原无格式文本,通用最重要;
慢慢有点靠近Unix的设计哲学的样子,简单就好;

3) 消停了的purplexsu
http://www.purplexsu.net/blog/article-133-01.html
“若要一天不得安,请客;若要一年不得安,盖房;若要一辈子不得安,娶姨太太。”
看了有四五年的博客中,有purplexsu的。生活就是一字:野;
可以不穿好的,可以不吃好的,但不能一个月不出去转悠;世界基本转个遍。
不过,终于来了消停的时候。
当有娃了,拉停你的一起出行。

4) 《奇特的一生》读后感 -时间管理
http://www.mifengtd.cn/articles/lyubishchev-time-management-reflection.html
这本书老早就看过,这篇读后感写的不错,分享一下;
亚历山大•亚历山德罗维奇•柳比歇夫(1890年4月5日——1972年8月31日),前苏联的昆虫学家、哲学家、数学家。毕业于圣彼得堡国立大学,一生发布了70余部学术著作,从分散分析、生物分类学到昆虫学等。

他在26岁时独创了一种“时间统计法”,通过记录每个事件的花销时间,通过统计和分析,进行月小结和年终总结,以此来来改进工作方法、计划未来事务,从而提高对时间的利用效率。期间他不断完善这一统计方法,并一直沿用了56年直到逝世。

柳比歇夫最伟大之处或许就在于他的时间统计法。简而言之,柳比歇夫时间管理法就是要记录时间、分析时间、消除时间浪费、重新安排自己的时间,是个人时间定量管理的方法。

5) 曾国藩每天必做的十二件事
http://www.hjtmt.com/603.html
点评我的体会:
静坐;这个我一直使用类似的,称之为冥想,我主要用它来放松全身的疲惫;每日工作完后,回家很累的时候,如果有20分钟的冥想,可保证晚上剩余的时间精力充沛。亲身体会,其它妙用暂不领会;话说就这一项,就值得我们去保持;
读书不二,以十页为率;我觉得同一类别的书读书不二更为合适;有时候时间是空闲的,但精力非满血状态,并不适合来读技术书籍。
日知所亡;读书笔记要有条理的记录下来;对于读书,现在正在入门阶段;一本好书,切不可只看一遍就完事,第一遍算是整体了解;第二遍则是重点把握,并仔细做笔记,捎带思考。
对于如何读书,推荐这本书:《如何阅读一本书》
http://book.douban.com/subject/1013208/



6) 感到生活单调机械?20个小贴士重新激发你的生命活力
http://bestinme.org/20%E4%B8%AA%E5%B0%8F%E8%B4%B4%E5%A3%AB%E9%87%8D%E6%96%B0%E6%BF%80%E5%8F%91%E4%BD%A0%E7%9A%84%E7%94%9F%E5%91%BD%E6%B4%BB%E5%8A%9B/
人大部分抑郁、压抑情绪都是由身体的物理特性所致;没什么大不了,出去活动活动、跑跑跳跳,回来啥事都没有;

开心笑:
英语老师暑假带女儿去找在外地工作的爸爸,在车上,,,,咯咯,,,,
女儿问:麻麻,我们去那儿要住哪呀?
妈妈:我们住租的房子呀!
女儿想了一会儿,又问:那猪住哪儿呢?

Linux 系统中僵尸进程

Linux 系统中僵尸进程和现实中僵尸(虽然我也没见过)类似,虽然已经死了,但是由于没人给它们收尸,还能四处走动。僵尸进程指的是那些虽然已经终止的进程,但仍然保留一些信息,等待其父进程为其收尸。配图源自 Flickr




僵尸进程如何产生的?

如果一个进程在其终止的时候,自己就回收所有分配给它的资源,系统就不会产生所谓的僵尸进程了。那么我们说一个进程终止之后,还保留哪些信息?为什么终止之后还需要保留这些信息呢?

一个进程终止的方法很多,进程终止后有些信息对于父进程和内核还是很有用的,例如进程的ID号、进程的退出状态、进程运行的CPU时间等。因此进程在终止时,回收所有内核分配给它的内存、关闭它打开的所有文件等等,但是还会保留以上极少的信息,以供父进程使用。父进程可以使用 wait/waitpid 等系统调用来为子进程收拾,做一些收尾工作。

因此,一个僵尸进程产生的过程是:父进程调用fork创建子进程后,子进程运行直至其终止,它立即从内存中移除,但进程描述符仍然保留在内存中(进程描述符占有极少的内存空间)。子进程的状态变成
EXIT_ZOMBIE
,并且向父进程发送SIGCHLD 信号,父进程此时应该调用
wait()
系统调用来获取子进程的退出状态以及其它的信息。在 wait 调用之后,僵尸进程就完全从内存中移除。因此一个僵尸存在于其终止到父进程调用 wait 等函数这个时间的间隙,一般很快就消失,但如果编程不合理,父进程从不调用 wait 等系统调用来收集僵尸进程,那么这些进程会一直存在内存中。

在 Linux 下,我们可以使用 ps 等命令查看系统中僵尸进程,僵尸进程的状态标记为‘Z’:




产生一个僵尸进程

根据上面的描述,我们很容易去写一个程序来产生僵尸进程,如下代码:

#include <stdio.h>
#include <sys/types.h>

int main()
{
//fork a child process
pid_t pid = fork();

if (pid > 0)   //parent process
{
printf("in parent process, sleep for one miniute...zZ...\n");
sleep(60);
printf("after sleeping, and exit!\n");
}
else if (pid == 0)
{
//child process exit, and to be a zombie process
printf("in child process, and exit!\n");
exit(0);
}

return 0;
}


父进程并没有写 wait 等系统调用函数,因此在子进程退出之后变成僵尸进程,父进程并没有为其去收尸。我们使用下面命令编译运行该进程,然后查看系统中进程状态:

guohailin@guohailin:~/Documents$ gcc zombie.c -o zombie
guohailin@guohailin:~/Documents$ ./zombie
in parent process, sleep for one miniute...zZ...
in child process, and exit!

# 打开另一个终端:
guohailin@guohailin:~$ ps aux | grep -w 'Z'
1000      2211  1.2  0.0      0     0 ?        Z    13:24   6:53 [chromium-browse] <defunct>
1000      4400  0.0  0.0      0     0 ?        Z    10月16   0:00 [fcitx] <defunct>
1000     10871  0.0  0.0      0     0 pts/4    Z+   22:32   0:00 [zombie] <defunct>

从上面可以看出,系统中多了一个僵尸进程。但如果等父进程睡眠醒来退出之后,我们再次查看系统进程信息,发现刚才的僵尸进程不见了。

guohailin@guohailin:~/Documents$ ./zombie
in parent process, sleep for one miniute...zZ...
in child process, and exit!
after sleeping, and exit!
guohailin@guohailin:~/Documents$ ps aux | grep -w 'Z'
1000      2211  1.2  0.0      0     0 ?        Z    13:24   6:53 [chromium-browse] <defunct>
1000      4400  0.0  0.0      0     0 ?        Z    10月16   0:00 [fcitx] <defunct>

这是为什么呢?父进程到死都也没有为其子进程收尸呀,怎么父进程退出之后,那个僵尸进程就消失了呢?难道父进程在退出时会为子进程收拾吗?其实不然....真正的原因是:父进程死掉之后,其所有子进程过继给 init 进程,init 进程成为该僵尸进程的新进程,init 进程会周期性地去调用 wait 系统调用来清除它的僵尸孩子。因此,你会发现上面例子中父进程死掉之后,僵尸进程也跟着消失,其实是 init 进程为其收尸的!

怎样避免僵尸进程的产生

不能使用 kill 后接
SIGKILL
信号这样的命令像杀死普通进程一样杀死僵尸进程,因为僵尸进程是已经死掉的进程,它不能再接收任何信号。事实上,如果系统中僵尸进程并不多的话,我们也无需去消除它们,少数的僵尸进程并不会对系统的性能有什么影响。

那么在编程时,如果能避免系统中大量产生僵尸进程呢?根据上面描述的,子进程在终止时会向父进程发 SIGCHLD 信号,Linux 默认是忽略该信号的,我们可以显示安装该信号,在信号处理函数中调用 wait 等函数来为其收尸,这样就能避免僵尸进程长期存在于系统中了。示例代码如下:

#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>

sig_atomic_t child_exit_status;

void clean_up_child_process(int signal_num)
{
/* clean up child process */
int status;
wait (&status);

/* store its exit status in a global variable */
child_exit_status = status;
}

int main()
{
/* handle SIGCHLD by calling clean_up_child_process  */
struct sigaction sigchild_action;
memset(&sigchild_action, 0, sizeof(sigchild_action));
sigchild_action.sa_handler = &clean_up_child_process;
sigaction(SIGCHLD, &sigchild_action, NULL);

/* fork a child, and let the child process dies before parent */
pid_t c_pid;
c_pid = fork();
if (c_pid > 0)
{
printf("in parent process, and sleep for on mininute...zZ...\n");
sleep(60);
}
else if(c_pid == 0)
{
printf("in child process, and exit now\n");
exit(0);
}
else
{
printf("fork failed!\n");
}

return 0;
}


参考资料

http://en.wikipedia.org/wiki/Zombie_process

http://simplestcodings.blogspot.com/2010/10/story-of-zombie-process.html

http://www.howtogeek.com/119815/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: