之前是做几年的Windows c++开发,Linux下的经验不够丰富,导致我在看QEMU代码时,有些地方还需要回头学习Linux操作系统的实现机制才能更准确理解。学习Linux操作系统时泛泛地看了很多书籍,好像明白了,但是要深刻理解,以这平庸的智商我觉得还是要多看代码多码代码。闲话少说,来看下os-posix.c中的一个函数,叫os_daemonize(),从名字上我们就知道是要搞一个守护进程,代码如下:
void os_daemonize(void)
{}
我们主要关注fork和setsid函数的调用。第一个fork调用之后,父进程调用exit退出,然后子进程调用setsid,接着子进程又调用fork产生一个孙进程(是我发明的叫法吗?),然后子进程退出,显然孙进程变成了最后的守护进程。问题是为什么要这么做?
首先我们知道守护进程是在后台运行的,没有控制终端(Controlling Terminal)和它关联,当我们执行QEMU命令时,一般是从Shell里敲命令执行,这时候这个进程就是有了控制终端。第一个fork后子进程调用setsid,把自己放到了一个新的session里,和父进程不属于一个session,也就是没有和控制终端有联系了。父进程退出后,子进程会重新找父进程,这个父进程就是init进程,这样也不会有僵尸进程的出现。其实这个时候子进程已经是个守护进程了,但它为啥不辞劳苦又去调用一次fork生成孙进程然后自尽呢?原因是子进程在调用setsid后,它成为了新的session的leader,虽然目前它没有和控制终端关联了,但是作为一个session的leader,它是有机会再去关联上一个控制终端的,如果代码里不小心的某些操作使得子进程又和某个控制终端关联上,那就不是守护进程本来该有的样子了。所以为了万无一失,子进程就fork了一个孙进程,孙进程和子进程同属一个session,但孙进程自己没有调用setsid,它不是session的leader,子进程退出后,孙进程再也没有机会去关联一个控制终端了,因为能关联上控制终端的只能是session的leader进程,这样一来,孙进程就满足了守护进程的一些特性,作为最后的守护进程继续运行。
总结一下,os_daemonize()做的就是两件事:
1. 生成一个子进程,和控制终端撇开关系,通过调用fork和setsid函数来达到;
2. 子进程生成一个孙进程,孙进程没有调用setsid,子进程退出,因为孙进程不是session的leader,它再也没有机会和控制终端关联上,它就是我们想要的守护进程;
关于daemon两次fork的讨论,可参考这里:
http://stackoverflow.com/questions/881388/what-is-the-reason-for-performing-a-double-fork-when-creating-a-daemon
关于session,可参考这里:
http://www.informit.com/articles/article.aspx?p=397655&seqNum=6
我就不弄超链接了,因为CSDN会对含有超链接的文章进行审核,搞得博文经常发布几个小时后都看不到。
版权声明:本文为博主原创文章,未经博主允许不得转载。
原文地址:http://blog.csdn.net/flyforfreedom2008/article/details/47306821