究竟是什么占用了我们的内存

 
1、            前言        其实这个问题很早以前遇见过,也解决了。只是最近在我所属的山东A+P个贷系统推广项目上又发现新人在代码上出现了同样的问题。为了避免这样的问题再出现。所以在这里把这个问题给大家共享一下。

2、            究竟还有什么东西占用了我们的系统资源 相信在我们公司很多做C语言的程序员都遇到过这样一个问题,就是系统莫名其妙的资源被用完了。一般情况下,我们都会去从文件的描述符是不是没有fclose、或者是SOCKET资源是不是没有close,等这些方面去考虑程序的问题。但是经过一系列的周密检查发现程序在这些方面并没有出现问题。那么这个时候我们不得不问这样一个问题?究竟还有什么占用了我们的系统资源?回答是僵死进程。当然这个进程说法也有另外一个叫法是僵尸进程。至于为什么会有这么2个名字的叫法我们姑且不理会。我猜测是一种趣味翻译吧。

3、            怎么查看系统的僵尸进程 在上面我们说到了僵尸进程会耗尽我们的系统资源,那么我们如何查看是否存在僵尸进程呢?
您可以尝试执行ps –u 用户名 或者是 ps –ef
如果在ps 命令的返回列表信息中出现了类似如下的信息
2621   ttyp3    00:00:00 alscore
2624       -    00:00:00 <defunct>
2640       -    00:00:00 <defunct>
2642       -    00:00:00 <defunct>
2644       -    00:00:00 <defunct>
2646       -    00:00:00 <defunct>
2648       -    00:00:00 <defunct>
2694       -    00:00:00 <defunct>
那你的系统可能有麻烦了。 一般最后一列我们应该看到是进程名,这里的<defunct>就是僵尸进程了。

4、            僵尸进程如何产生的 为了说明这个问题,我得先给大家阐述一下UNIX操作系统对于S IGCHLD信号的处理。如果你认真细心的阅读过UNIX环境高级编程或者是阅读过关于LINUX操作系统内核实现中关于$linux11/kernel/exit.c中的do_eixt()函数的实现细节。你会很清楚认识到当一个进程正常或异常终止时,内核必然向其父进程发送SIGCHLD信号。当子进程发送完毕信号后进程的状态就会被操作系统的内核设置为僵尸状态( TASK_ZOMBIE) ,意味着已经是准备死亡的进程,但是作为内核来看这个进程依然是一个可以调度的进程,在进程调度的进程列表task_struct中依然存在。所以子进程还占用这个系统资源。子进程终止是个异步事件(这可以在父进程运行的任何时候发生),所以认为这种信号也是内核向父进程发的异步通知。如果子进程发送的S IGCHLD这个信号没有得到父进程的处理的话,那么这个子进程就依然还在可调度的进程列表中。只有当父进程处理了这个信号。子进程的所有资源才会完全被释放。这是我们首先要认识的一个关键地方。
那这就说明出现当父进程没有明确设置关于SIGCHLD信号的处理方法的话,那么就会导致僵尸进程的出现。

可能有点晦涩难以理解,从对这个子进程的名字的翻译来看,我想了一个比较形象的比喻来说明这个问题
当父亲的儿子死了后,就变成了什么都不做的僵尸了。父亲必须为儿子处理后事。就是所谓的收尸。那么上面说到的父进程处理SIGCHLD信号就是收尸的事。只有收尸了后才会完全释放所有的资源。

         谈到这里基本上已经从理论上给大家阐述了僵尸进程如何产生的以及它给我们的程序带来的一些问题。但是我认为还需要实际的环境的模拟才能更加深刻的认识这个问题,理论要联系实践嘛,千万不要偷懒哦。你准备好了吗?让我们一起进入下一章进行实践练习。

5、            让我们来实践一下 为了让大家更加深入掌握这个问题,我为大家准备了DEMO代码。
请大家参考附件文件demo.c
在这个文件中的第7行 有这么一个代码signal(SIGCHLD, SIG_IGN);
这个代码的含义就是父进程设置SIGCHLD信号处理方式是忽略处理,当然你可以设置一个处理函数,在处理函数中务必调用(wait 簇)函数去处理子进程。这个就真正收尸的过程。有兴趣看如何收尸处理的(wait 簇)函数 可以参考$linux11/kernel/signal.c 中的do_signal函数。
注意:忽略处理和不处理是2码事。
1.忽略处理 就相当于是父亲直接把死了的儿子丢了或者是直接烧了,至少说明了父亲还是要做事的。只是说这个后事办得隆不隆重而已。
2.不处理 就相当于是这个父亲连它儿子真的连烧都不烧了。

编译该程序cc –o demo demo.c运行demo  结果令人非常兴奋,程序居然正常运行了365天。

现在我们把第7行代码 signal(SIGCHLD, SIG_IGN); 注释掉。

再次编译程序 cc –o demo demo.c运行demo  结果让人非常沮丧,我一只烟还没抽完,就一直不停的出现for失败,看来系统资源没有了。
当然我希望你在运行错误程序的时候别抽烟,花点时间另外再开一屏netterm去不停执行ps –u 用户名看看<defunct>进程的数量是不是一直在增加。这样你会有意外的收获。

OK,从这里demo.c代码中我们用事实去证明了 父进程没有处理SIGCHLD信号是会产生僵尸进程的。

         另外也给大家附加了另外一个比较简短的说明僵尸进程的产生的示例代码demo_fork.c,以方便大家理解。

顺便在这里多说一句,注意我给的这个demo.c代码是个长期一直运行的程序(驻留程序、守护程序)就会出现这样的问题。但是如果这个代码里面的循环是个有限的for(i = 0; i < 10; i++)的话,其实僵尸进程也会产生的。但是当我们的demo.c程序结束后 僵尸进程也就全部没了。那么只会短暂的看到进程列表中会出现僵尸进程的情况。原因就是在于父进程消亡后,所有的由demo产生的子进程都统一变成孤儿进程(虽然这些子进程都是僵尸进程但是他们还是算是进程的) 由操作系统核心进程 init 统一收养。系统核心进程init是处理了SIGCHLD信号的,所以所有的被init接管收养的孤儿僵尸进程的资源也就全部释放了。

6、            我们再继续深入思考一下: 在 demo.c代码中仅仅是证明了关于fork会出现僵尸进程。其实是没有证明 exec 簇函数或者system函数创建的子进程有没有可能导致僵尸进程的。但是从LINUX原代码中关于exec 簇函数的实现分析来看,它也同样复制了父进程大部分的进程数据结构(task_struct)到内核中。所以它同样也面临相同的处理的问题。
但是由于时间关系和我们经常应用fork来说,我这里就没写出关于exec簇函数创建子进程是否会导致僵尸进程的示例代码。有兴趣的朋友可以尝试一下,不过记得把测试的结果数据友情的给我发送一份。

7、            结束语 欢迎大家能邮件指出文中的错误或者相互讨论UNIX编程中其它的问题,大家共同进步。

                                                                           天用唯勤  阳凌
                                                                           yl.tienon@gmail.com
                                                                           2007-6-30
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值