2009年8月12日星期三

僵尸进程的产生和避免,以及wait,waitpid的使用

转篇文章 归档

在fork()/execve()过程中,假设子进程结束时父进程仍存在,而父进程fork()之前既没安装SIGCHLD信号处理函数调用 waitpid()等待子进程结束,又没有显式忽略该信号,则子进程成为僵尸进程,无法正常结束,此时即使是root身份kill -9也不能杀死僵尸进程。补救办法是杀死僵尸进程的父进程(僵尸进程的父进程必然存在),僵尸进程成为”孤儿进程”,过继给1号进程init,init始终会负责清理僵尸进程。

僵尸进程是指的父进程已经退出,而该进程dead之后没有进程接受,就成为僵尸进程.(zombie)进程。
defunct进程只是在process table里还有一个记录,其他的资源没有占用,除非你的系统的process个数的限制已经快超过了,zombie进程不会有更多的坏处。

产生原因:
1.在子进程终止后到父进程调用wait()前的时间里,子进程被称为zombie。
2.网络原因有时会引起僵死进程。

解决方法:
1.设置SIGCLD信号为SIG_IGN,系统将不产生僵死进程。
2.用两次fork(),而且使紧跟的子进程直接退出,是的孙子进程成为孤儿进程,从而init进程将负责清除这个孤儿进程。


怎样产生僵尸进程的:
一个进程在调用exit命令结束自己的生命的时候,其实它并没有真正的被销毁,而是留下一个称为僵尸进程(Zombie)的数据结构(系统调用exit,它的作用是使进程退出,但也仅仅限于将一个正常的进程变成一个僵尸进程,并不能将其完全销毁)。在Linux进程的状态中,僵尸进程是非常特殊的一种,它已经放弃了几乎所有内存空间,没有任何可执行代码,也不能被调度,仅仅在进程列表中保留一个位置,记载该进程的退出状态等信息供其他进程收集,除此之外,僵尸进程不再占有任何内存空间。它需要它的父进程来为它收尸,如果他的父进程没安装SIGCHLD信号处理函数调用wait或waitpid()等待子进程结束,又没有显式忽略该信号,那么它就一直保持僵尸状态,如果这时父进程结束了,那么init进程自动会接手这个子进程,为它收尸,它还是能被清除的。但是如果如果父进程是一个循环,不会结束,那么子进程就会一直保持僵尸状态,这就是为什么系统中有时会有很多的僵尸进程。

怎么查看僵尸进程:
利用命令ps,可以看到有标记为Z的进程就是僵尸进程。
怎样来清除僵尸进程:
1.改写父进程,在子进程死后要为它收尸。具体做法是接管SIGCHLD信号。子进程死后,会发送SIGCHLD信号给父进程,父进程收到此信号后,执行 waitpid()函数为子进程收尸。这是基于这样的原理:就算父进程没有调用wait,内核也会向它发送SIGCHLD消息,尽管对的默认处理是忽略,如果想响应这个消息,可以设置一个处理函数。
2.把父进程杀掉。父进程死后,僵尸进程成为"孤儿进程",过继给1号进程init,init始终会负责清理僵尸进程.它产生的所有僵尸进程也跟着消失。
===========================================
在Linux中可以用
ps auwx
发现僵尸进程
a all w/ tty, including other users 所有窗口和终端,包括其他用户的进程
u user-oriented 面向用户(用户友好)
-w,w wide output 宽格式输出
x processes w/o controlling ttys
在僵尸进程后面 会标注
ps axf
看进程树,以树形方式现实进程列表
ps axm
会把线程列出来,在linux下进程和线程是统一的,是轻量级进程的两种方式。
ps axu
显示进程的详细状态
===========================================
killall
kill -15
kill -9
一般都不能杀掉 defunct进程
用了kill -15,kill -9以后 之后反而会多出更多的僵尸进程
kill -kill pid
fuser -k pid
可以考虑杀死他的parent process,
kill -9 他的parent process
===========================================

一个已经终止,但是其父进程尚未对其进行善后处理(获取终止子进程的有关信息、释放它仍占用的资源)的进程被称为僵死进程(Zombie Process)。
避免zombie的方法:
1)在SVR4中,如果调用signal或sigset将SIGCHLD的配置设置为忽略,则不会产生僵死子进程。另外,使用SVR4版的sigaction,则可设置SA_NOCLDWAIT标志以避免子进程僵死。
Linux中也可使用这个,在一个程序的开始调用这个函数
signal(SIGCHLD,SIG_IGN);
2)调用fork两次。程序8 - 5 实现了这一点。
3)用waitpid等待子进程返回.
===========================================
zombie进程是僵死进程。防止它的办法,一是用wait,waitpid之类的函数获得
进程的终止状态,以释放资源。另一个是fork两次
===========================================
defunct进程只是在process table里还有一个记录,其他的资源没有占用,除非你的系统的process个数的限制已经快超过了,zombie进程不会有更多的坏处。
可能唯一的方法就是reboot系统可以消除zombie进程。
===========================================
任何程序都有僵尸状态,它占用一点内存资源(也就是进程表里还有一个记录),仅仅是表象而已不必害怕。如果程序有问题有机会遇见,解决大批量僵尸简单有效的办法是重起。kill是无任何效果的
fork与zombie/defunct"
在Unix下的一些进程的运作方式。当一个进程死亡时,它并不是完全的消失了。进程终止,它不再运行,但是还有一些残留的小东西等待父进程收回。这些残留的东西包括子进程的返回值和其他的一些东西。当父进程 fork()一个子进程后,它必须用 wait() 或者 waitpid() 等待子进程退出。正是这个 wait() 动作来让子进程的残留物消失。
自然的,在上述规则之外有个例外:父进程可以忽略 SIGCLD 软中断而不必要 wait()。可以这样做到(在支持它的系统上,比如Linux):
main()
{
signal(SIGCLD, SIG_IGN); /* now I don't have to wait()! */
.
.
fork();
fork();
fork(); /* Rabbits, rabbits, rabbits! */

现在,子进程死亡时父进程没有 wait(),通常用 ps 可以看到它被显示为“”。它将永远保持这样 直到 父进程 wait(),或者按以下方法处理。
这里是你必须知道的另一个规则:当父进程在它wait()子进程之前死亡了(假定它没有忽略 SIGCLD),子进程将把 init(pid1)进程作为它的父进程。如果子进程工作得很好并能够控制,这并不是问题。但如果子进程已经是defunct,我们就有了一点小麻烦。看,原先的父进程不可能再 wait(),因为它已经消亡了。这样,init 怎么知道 wait() 这些zombie 进程。
答案:不可预料的。在一些系统上,init周期性的破坏掉它所有的defunct进程。在另外一些系统中,它干脆拒绝成为任何defunct进程的父进程,而是马上毁灭它们。如果你使用上述系统的一种,可以写一个简单的循环,用属于init的defunct进程填满进程表。这大概不会令你的系统管理员很高兴吧?
你的任务:确定你的父进程不要忽略 SIGCLD,也不要 wait() 它 fork() 的所有进程。不过,你也未必要总是这样做(比如,你要起一个 daemon 或是别的什么东西),但是你必须小心编程,如果你是一个 fork()的新手。另外,也不要在心理上有任何束缚。
总结:
子进程成为 defunct 直到父进程 wait(),除非父进程忽略了 SIGCLD 。
更进一步,父进程没有 wait() 就消亡(仍假设父进程没有忽略 SIGCLD )的子进程(活动的或者 defunct)成为 init 的子进程,init 用重手法处理它们。

wait的函数原型是:
  
  #include /* 提供类型pid_t的定义 */
  #include
   pid_t wait(int *status)
  
  进程一旦调用了wait,就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程, wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。
  
  参数status用来保存被收集进程退出时的一些状态,它是一个指向int类型的指针。但如果我们对这个子进程是如何死掉的毫不在意,只想把这个僵尸进程消灭掉,(事实上绝大多数情况下,我们都会这样想),我们就可以设定这个参数为NULL,就象下面这样:
  
  pid = wait(NULL);
  
  如果成功,wait会返回被收集的子进程的进程ID,如果调用进程没有子进程,调用就会失败,此时wait返回-1,同时errno被置为ECHILD。
 

waitpid的函数原型是:


  简介
  waitpid系统调用在Linux函数库中的原型是:
  
  #include /* 提供类型pid_t的定义 */
  #include
  pid_t waitpid(pid_t pid,int *status,int options)

从本质上讲,系统调用waitpid和wait的作用是完全相同的,但waitpid多出了两个可由用户控制的参数pid和options,从而为我们编程提供了另一种更灵活的方式。下面我们就来详细介绍一下这两个参数:
  
  ● pid
  
  从参数的名字pid和类型pid_t中就可以看出,这里需要的是一个进程ID。但当pid取不同的值时,在这里有不同的意义。
  
  pid>0时,只等待进程ID等于pid的子进程,不管其它已经有多少子进程运行结束退出了,只要指定的子进程还没有结束,waitpid就会一直等下去。
  
  pid=-1时,等待任何一个子进程退出,没有任何限制,此时waitpid和wait的作用一模一样。
  
  pid=0时,等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid不会对它做任何理睬。
  
  pid<-1时,等待一个指定进程组中的任何子进程,这个进程组的ID等于pid的绝对值。

  ● options

  options提供了一些额外的选项来控制waitpid,目前在Linux中只支持WNOHANG和WUNTRACED两个选项,这是两个常数,可以用"|"运算符把它们连接起来使用,比如:


  ret=waitpid(-1,NULL,WNOHANG | WUNTRACED);


  如果我们不想使用它们,也可以把options设为0,如:


  ret=waitpid(-1,NULL,0);
  
  如果使用了WNOHANG参数调用waitpid,即使没有子进程退出,它也会立即返回,不会像wait那样永远等下去。
  
  而WUNTRACED参数,由于涉及到一些跟踪调试方面的知识,加之极少用到,这里就不多费笔墨了,有兴趣的读者可以自行查阅相关材料。
  
  看到这里,聪明的读者可能已经看出端倪了--wait不就是经过包装的waitpid吗?没错,察看<内核源码目录>/include/unistd.h文件349-352行就会发现以下程序段:
  
  static inline pid_t wait(int * wait_stat)
  {
   return waitpid(-1,wait_stat,0);
  }
  

  返回值和错误
  
  waitpid的返回值比wait稍微复杂一些,一共有3种情况:
  
  ● 当正常返回的时候,waitpid返回收集到的子进程的进程ID;
  
  ● 如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
  
  ● 如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;
  
  当pid所指示的子进程不存在,或此进程存在,但不是调用进程的子进程,waitpid就会出错返回,这时errno被设置为ECHILD

其它:

调用 wait&waitpid 来处理终止的子进程:
pid_t wait(int * statloc);
pid_t waitpid(pid_t pid, int *statloc, int options);

两个函数都返回两个值:函数的返回值和终止的子进程ID,而子进程终止的状态则是通过statloc指针返回的。

wait&waitpid 的区别是显而易见的,wait等待第一个终止的子进程,而waitpid则可以指定等待特定的子进程。这样的区别可能会在下面这种情况时表现得更加明显:
当同时有5个客户连上服务器,也就是说有五个子进程分别对应了5个客户,此时,五个客户几乎在同时请求终止,这样一来,几乎同时,五个FIN发向服务器,同样的,五个SIGCHLD信号到达服务器,然而,UNIX的信号往往是不会排队的,显然这样一来,信号处理函数将只会执行一次,残留剩余四个子进程作为僵尸进程驻留在内核空间。此时,正确的解决办法是利用waitpid(-1, &stat, WNOHANG)防止留下僵尸进程。其中的pid为-1表明等待第一个终止的子进程,而WNOHANG选择项通知内核在没有已终止进程项时不要阻塞。

wait&waitpid 区别

waitpid提供了wait函数不能实现的3个功能:

* waitpid等待特定的子进程, 而wait则返回任一终止状态的子进程;
* waitpid提供了一个wait的非阻塞版本;
* waitpid支持作业控制(以WUNTRACED选项).

用于检查wait和waitpid两个函数返回终止状态的宏:

这两个函数返回的子进程状态都保存在statloc指针中, 用以下3个宏可以检查该状态:

* WIFEXITED(status): 若为正常终止, 则为真. 此时可执行

o WEXITSTATUS(status): 取子进程传送给exit或_exit参数的低8位.

* WIFSIGNALED(status): 若为异常终止, 则为真. 此时可执行

o WTERMSIG(status): 取使子进程终止的信号编号.

* WIFSTOPPED(status): 若为当前暂停子进程, 则为真. 此时可执行

o WSTOPSIG(status): 取使子进程暂停的信号编号

2008年12月23日星期二

凌晨1点后

今天晚上特别冷,可能是我还没有进被窝的原因。反反复复的听着那首有关圣诞节的歌。不得不面对我撒的一个谎。我是不是会变的狠毒?笑。。。
照理说这么一个时间是看书学习的好时间阿,对我而言。也的确有很多工作压着。Jboss portal,AMQP,python,django。恩,如果明天还这样的话,就强制看书:)
刚才翻了一下我的CD,发现我真的相当的极品。。。 最近找到的一张很喜欢听的专辑,居然在4年前我就有CD了,怪不得有点似曾相似的感觉。话说我口味还真单一阿。
新房间大致收拾的可以住了,房子不错挺大,就是有点冷。从明天晚上(部队应该是今天)开始做俯卧撑,上周打球被mm鄙视上肢力量了。
ok喝口水,听完最后一遍,就去睡觉,明天还上班呢,能不能起的来呢 。是不是再找一集cowboy behop来看看呢,享受一下孤独呢:)

2008年12月17日星期三

498路无人售票车进站了

这两天一直坐498回学校。发现我很喜欢那班车,人不多,直接抵达学校。偶尔可以在后排找到座,可以安静的听着jazz,看着北京4环的风景。抱着自己所有洗漱用具换洗衣服,感觉一个包,一首歌,一个人就可以去任何地方了。很多年没有这样的心境了:)今次一定要好好成长。星座上说明年是我默默成长的一年,感觉还真不假。
前几天和一个同学在msn上聊天,突然说到2年后是否能见到一个闪耀的你。然后我问闪耀是什么意思?然后那边回答说就是很赞很不错。我想大概我在她印象里还是当年那个人,如果现在见到我肯定会失望吧。我觉得自己是一个自恋的人,但我差不多有1年多没有好好照过镜子了。都不知道现在自己的样子,然后我去照了一下镜子,列举了一下目前的人身,发现现在的我实在是很不赞阿 :/ 不过就在这一刻,我却觉得很开心,终于找到一件值得努力的事情了。人生有了点目标。从小就是这样只要我有所保留就一定不能完美。这次一定要认真做人,做事。一点都马虎不得。最近有点心不在焉,早上居然把洗面奶和牙膏反用,晚上坐车差点坐过站,订错机票亏了600多,开会踩坏一个花盆== 但我相信一定会好起来的。在这里告诉自己要努力,认真。
从小就想当飞行员,要做一个勇敢,自由,果断的人。是我的绝对是我的,不是我的又何必如此在意,只要自己全力争取了就行了。

圣诞前终于要把项目结了,可恶的llim已经把我分到下一个项目中去了。。。也不给个几天闲,整理一下心情。Anyway 有挑战总是好的。 希望dli界面能早点做好,别再拖了不然圣诞前就 结不了了。

2008年12月7日星期日

转篇歌词

Madagascar Lyrics
Artist(Band):Guns N' Roses
Review The Song (5) Print the Lyrics

I won't be told anymore
That I've been brought down in this storm
And left so far out from the shore
That I can't find my way back, my way anymore

Oh no I won't be told anymore
That I've been brought down in this storm
And left so far out from the shore
Oh, that I can't find my way back, my way anymore


Oh no, I...

I...


Forgive them that tear down my soul
Bless them that they might grow old
And free them so that they may know
That it's never too late

For the many times, what seemed like a memory
I've searched and found the ways you used to lure me in
I found the way, oh, why it had to be
Mired in denial and so afraid

If we ever find it’s true
That we have the strength to choose
Our freedom or the chains
We held together


(Quotes)
I'm gonna tell you a story
Stand up for righteousness!
What?
Stand up for justice!
What?
Stand up for truth!
How can a person grow up with all this around them?
You got to call on that something
Where does it come from?
That can make a way out of no way
All this hatred?
What we've got here is..
Fear!
That power that can make a way out of now no way
Failure to communicate
I tell ya I seen the lightning...I've heard the thunder roll!
Everybody's acting like we can do anything and it don't matter what we do. Maybe we gotta be extra careful because maybe it matters more than
we even know..
Sometimes...
Hatred isn't somethin' you're born with
I feel discouraged
It gets taught
Sometimes I feel discouraged
I felt this fear
He promised never to leave me, never to leave me alone, no never alone, no never alone!
Let's get something straight, alright?
Promised never to leave me!
This whole thing was fucked up
Never to leave me alone!
All men betray. All lose heart!
I don't want to lose heart!...I want to believe
Black men and white men
Together at the table of brotherhood!
I have a dream
Free at last! Free at last! Thank God almighty!! WE ARE FREE AT LAST!!


I won't be told anymore
That I've been brought down in this storm
And left so far out from the shore
That I can't find my way back, my way anymore

Oh no I won't be told anymore (If we ever find its true)
That I've been brought down in this storm (That we have the strength to choose)
And left so far out from the shore (Our freedom or the chains)
That I can't find my way back, my way anymore (We held together)


突然发现cg汉化组在昨天发布了 Tag Force 3的汉化版本,赞但是在么完全没心情玩了。

2008年12月6日星期六

写在出门前0.02秒

前几天我终于想明白了一件困扰我十几年的事情,那就是:到底如何刷牙,横着刷还是竖着刷,到底哪个是正确的。
很小的时候,我家里人都横着刷牙,所以我也是横着刷,但是别人都说竖着刷是正确的,但是明明横着刷比较方便阿,而且比较方便清洁到里面的牙齿阿。然后自从离开上大学离开家以后,我就强行纠正了我的刷牙方法,改用竖刷法 :)
虽然相当不习惯但感觉还是不错,心想这下牙齿就能很干净了。下面插播一段前几天的经历。
#-------------------------------------------------------------------------------------------------#
前一阵子发现我牙齿碰到甜的东西,比如巧克力之类的就会疼,妈呀我是不是有蛀牙了。。。 我也不怎么吃零食阿,从小就没这习惯阿。跑去买了一个电动牙刷,每天狂刷。但是没什么效果,于是就跑去看牙医,到那边发现除了我都是看智齿的mm。一下排到了最后,那个郁闷阿。检查的过程颇为简单,漱口,张嘴,5秒后医生就说可以了,然后告诉我牙齿非常健康,冏阿。就是有点牙石,这里学了一个新名词牙石。总之就是有些地方刷牙没有刷到,然后软垢堆积,然后就变硬产生了牙石,然后医生三下五除二,用一跟金属的镊子状物体在我嘴里狂刮几下,就让我下台走人了。。然后我就奇怪了,为什么我不断修正的刷牙法都没刷到那些地方呢,理论上竖刷法不是因该是完美的么。于是我就留心了一下贴在诊所墙上的名字类似《正确刷牙法》的一张图。
#--------------------------------------------------------#
发现根本不是绝对的竖刷或是横刷,处理几乎每颗牙齿的时候都会同时用到横竖两种方法,这么说我岂不是被欺骗了好多年阿。。。但是回想起来也没有一个人很认真的告诉我刷牙方法。

我又想到了方法无非是达到一个目的的手段和过程。最终目的是要让牙保持清洁,又何必在乎过程是如何的呢。我只要想如何能把牙刷干净不就好了么。 我向来都是追求结果的人阿,在么连这个都没想到呢。生活处处都得靠自己想阿,真麻烦。分析结果就是我庸人自扰了很多年。然后又觉得我的生活情商真的很低阿。比较迂腐守旧,为了遵守一个约定或是准则就算绕大圈也绝对不能触犯这个准则。不过生活情商低换来了我工作情商比较高的局面:),以前经常在个工作学习中遇到这样的情况,发现其实我不用这么看书或是用这个方法就能搞定。我和别人的情况不同,有自己的方法,就比如我高中喜欢回家先睡觉,9点10点再起来看书写字,但如果要我回家就努力看书,估计我也是在看什么杂七杂八的小说杂志。虽然不同但是最后我还是能和别人一样高效的学习。其实还想到了很多,但是发现自己的文笔尘封了5年,措辞造句都是狗屁不通了。 回头再多写点有深度的。

这两天在听枪花新专辑,觉得这次听怎么这么像bon jovi了呢,有点变质了阿。难道我都忘了那个让人不能忍受但是却让人离不开的音调? 于是乎打算去翻出旧cd来听下,但是回头一想,我已经没有cd机了阿。。。开始怀念我的松下cd机了,是不是因该再去买一个,至少不能浪费了我这一小箱CD阿,想到CD我有想起了我家一抽屉磁带,以及很久前坏掉的sony walkman,也不知道那些磁带还能不能听呢。越来越怀旧了阿。突然想起我的旧ipod了,没了cd的日子都是他配我度过的阿,因该好好保养一下了。

这出门之前写太长了,赶紧闪人了。

2008年12月2日星期二

加油写blog

决定就算我一个月就写一篇 也要坚持写下去

然后,时间间隔太久 游记就免了。。
只记得西湖和玄武湖都好漂亮,如果有一天去南京或是杭州上班的话,一定也在情理之中。
我真的会去南京或是杭州么。。。? 我想要怎么样的生活呢?
好吧2008年12月3日刚到15分钟的这一刻我觉得:
对自己要不断超越自我,克服自己的弱点,不断求索,改善自己。
对心爱的女人要呵护有加,能安心的抱着她在湖边吹风谈天,带着她天南地北笑傲江湖,
一起看电影听音乐,一起感动一起伤心。
对待家人,要尽心尽力,让他们开心。
那样因该就算是我追求的生活了吧。

最近安静下来思考的时候发现,真的每个人的世界都是不同的,我的世界,教练的世界,llim的世界,威哥的世界,小白的世界,mm的世界,jiabo的世界,willows的世界,敏捷的世界,osier的世界,hliu的世界。。。等等好多。llim的努力和等待,小白的追寻,威哥的愿望,每个人面临不同的问题,要做不同的选择。以前我一直没有意识到这个问题,我一直觉得或是下意识觉得,世界都是一样的,面对的都是升学,考试。到底在哪里我们开始有了不同? 最后我想明白了,不同一直存在,只是我考虑的事情越来越多了。原来我这才长大,是属于晚熟类型的呢。。。假了点,那如果不是晚熟,那就只能是自恋咯。