Archive for 六月, 2009

Linux的精灵(daemon)进程和僵尸(zombie)进程

Posted by jcadam - 27/06/09 at 04:06 下午

有些日子没有跟工程师一起工作了。昨天星期五,我回到川崎的办公室和同事一起结果一个客户反馈的问题。这个工作却又让我怀念以前作研发的那些时光。突然觉得有些东西还是不应该被忘记,就写在这里作笔记了。

守护进程(Daemon)

守护进程——我情愿把他叫做精灵进程——是多种操作系统中的一种常驻程序。常驻的意思是,在系统运行期间,这些进程都一直存在。很明显,大多数服务器程序都是精灵进程。在这里我要说一说自己知道关于精灵进程的方法,和与此相关的一些技巧。

如何产生一个精灵进程?

Linux系统启动以后,它通常启动的第一个进程就是init进程。init进程的进程ID是1,并且它会一直在系统运行期间存续。同时,Linux对进程的管理策略有两个比较特殊的地方,其中之一就是,当一个进程的父进程结束,它的子进程如果还继续运行,那么,子进程就会被init进程收养。也算就是说,Linux对进程的管理策略,其实是一直在努力维护一棵树。利用这一点,就可产生伴随init一直存在的精灵进程。代码例子如下:

/* file: daemon_sample.c */
#include
#include
#include
#include 

int main ()
{
	pid_t	pid,sid;

	/* fork to leave parent */
	pid = fork ();
	if (pid < 0) {
		printf ("failed to fork!\n");
		exit (EXIT_FAILURE);
	} else if (pid > 0) {
		/* parent process go to exit */
		exit (EXIT_SUCCESS);
	}

	/* child process */
	chdir("/");
	sid = setsid ();
	if (sid < 0) {
		printf ("Failed to set session id.\n");
		exit (EXIT_FAILURE);
	}
	/* ensure all files created by itself are accessible */
	umask (0);

	printf ("Daemon adopted by parent %d\n", getppid());

	/* daemon loop */
	while (1) {
		sleep (1);
	}
}

产生一个精灵进程的过程需要有一下几步:

  1. 从启动该程序的进程中fork出来一个子进程;如果子进程产生成功,那么就将父进程结束
  2. 子进程需要作一些处理来保证自己作为精灵的地位;改变当前工作目录;利用setsid系统调用重新设置一个session;利用umask系统调用消除父进程对于文件控制的影响,从而保证精灵可以正确的访问它自己生成的各种文件——其中比较重要的是IPC
  3. 子进程需要启动一个自身处理的循环

这是一个非常简单的例子,通常情况下,子进程还需要关闭标准输入输出等等工作。与此相关的详细讨论可以查看
http://www.netzmafia.de/skripten/unix/linux-daemon-howto.html
这里有一个可以下载到本地的PDF版本
http://www.google.co.jp/url?sa=t&source=web&ct=res&cd=2&url=http%3A%2F%2Fwww.cs.aau.dk%2F~adavid%2Fteaching%2FMTP-05%2Fexercises%2F10%2FLinux-Daemon_Writting.pdf&ei=W7pFSreDHoTW7APC0_Eh&usg=AFQjCNHyZnXmdKL8ebxJ8UXQsYeGHiqRwQ&sig2=7Of2PvUnmTm8xqZifI-aAw

怎样判断一个进程是否是精灵进程?

恩,奇怪的地方就在与此了。如果用如下命令编译上面的简短程序,你会发现一处不太符合想象的地方。

$ gcc -o daemon_sample daemon_sample.c

在终端窗口中运行这个程序,然后我们作一些观察。

$ ./daemon_sample

按照上面我叙述过的内容,这个程序的将被init进程收养。那么,理所当然的它的父进程ID应该是“1”。然而,这个程序运行时却抛出了这样一句话。

Daemon adopted by parent 29914

这里的29914是通过getppid()取得的,难道有什么不对头?为了确认情况,再用ps查看一下究竟发生了什么事情。

$ ps -e -o pid,ppid,cmd,tty | grep daemon_sample
29915 1 ./daemon_sample ?

利用ps命令的-o选项,可以查看进程的pid,ppid,命令行和运行终端的信息。显而易见,这个精灵进程事实上的ppid是“1”。那么,为什么getppid()不能返回正确的值?难道这里是个BUG?就目前Linux内核的状况来说,这不是一个BUG,查看getppid的手册页可以看到,getppid并不保证能够取得一个进程被收养以后的父进程ID。或者有兴趣的xdjm可以尝试一下将这个功能升级一下:)。

换句话说,不能使用getppid来判断一个进程时候是精灵进程。那么,应该怎样判断?

再看上面ps的输出结果,你会发现这个进程的tty域是一个问号。这表明,该进程并没有被attach到任何终端设备上去。事实上所有的精灵进程都有这个特性。利用ps做一个验证吧。

$ ps -e -o pid,ppid,cmd,tty | grep daemon
898 1 /sbin/udevd --daemon ?
2464 1 /bin/dbus-daemon --system ?
3305 1 avahi-daemon: running [capr ?
3829 1 //bin/dbus-daemon --fork -- ?
29915 1 ./daemon_sample ?
31584 29871 grep --color=auto daemon pts/1

这里有ps命令的结果,它被attach到伪终端pts/1。所以,可以利用这个特性来判断一个进程是否是精灵进程。

if ((devtty = open ("/dev/tty", O_RDWD)) < 0) {
   printf ("This is a daemon\n");
} else {
   printf ("This is not a daemon\n");
}

在UNIX论坛上有一个关于这个话题的旧帖子,地址如下:
http://www.unix.com/high-level-programming/72479-how-find-if-process-daemon.html
精灵进程大致就是这样。

僵尸进程

僵尸进程其实是个错误。

这个错误产生的原因在于,子进程已经结束,但是父进程却没有对它进行清理。僵尸进程会被init收编,并且init会料理这些僵尸的“善后事宜”。我们可以写一个很蹩脚的程序来产生一个货真价实的僵尸进程。僵尸来了!

/* file: zombie.c */
#include
#include
#include 

int main ()
{
	pid_t child_pid;

	/* Create a child process. */
	child_pid = fork ();
	if (child_pid > 0) {
	   /* This is the parent process. Sleep for a minitue. */
	   sleep (60);
	} else {
	   /* This is the child process. Exit immediately. */
	   exit (EXIT_SUCCESS);
	}
	return 0;
}

编译并运行这个程序,在程序运行期间,利用ps查看它的状态,你会得到下面的结果:

$ ps -e -o pid,stat,cmd | grep zombie
4302 S+ ./zombie
4303 Z+ [zombie]

你会发现进程4302——也就是僵尸的父进程——还没有结束,而进程4303进程的状态已经变成了“Z”。这也就意味这该程序产生的子进程已经变成了僵尸。

在一般的桌面系统上,即使产生僵尸进程也不是什么大不了的事情,因为,系统资源并不紧张的情况下,僵尸最终会被init清理。但是,在高负载的服务器或者资源非常有限的嵌入式系统中如果大量出现僵尸,那会是个一个麻烦。所以,避免产生僵尸进程的方法就是在父进程中正确的处理wait和SIGCHLD信号,具体做法google一下可以出很多结果,这里就不再累述了。

嘿~,如果您喜欢我的博客,您可以通过RSS.链接将本博客的最新文章传输到您喜欢的阅读器。 Subscribe with Google 
订阅到鲜果 订阅到抓虾

民主实践的感受和思考

Posted by jcadam - 13/06/09 at 09:06 下午

参与深圳LUG的讨论后,我得到的一些对民主实践的新的感受。或者,这些感受也有可能与“民主”没有什么关系,现在我只是想把他记录下来。

开放源代码社区和一些类似的自治团体,应当是现在中国社会实践小范围民主自由制度的摇篮。特别是开源社区的氛围和一些约定俗称的制度,非常有利于让以民主方式讨论问题的形式逐步展开。但是,真正的将这个想法付诸时间之后,我才发现这其中却显露出来很多问题。

第一,社区组织形成通常来自于一些爱好者的热情支持,因此,社区在发展过程中会有可能走不出广泛民主实践的道路。比如,我比较关注的深圳LINUX爱好者团体,创建伊始到现在一直靠几位创始人和核心活动成员的的热情得以存在。而且这种松散的线上社区一般都没有明确的行动纲领(guideline)和目标,于是有可能将社区活动最终办成“圈内”人士的自娱自乐。这可以说是开源软件社区相比较商业软件形成的联盟存在的先天性不足吧。

第二,文化和政治环境的限制让民主制度的创新受到限制。我不敢说这是社区实践民主缺乏的首要因素,但至少这一点也是关键因素。说的大一点,即是“民主制度究竟应该怎样搞”这个问题还没有得到解决。我想这个问题的形成,正反应的中国政治教育的失败。政治本身不是一件可怕的事情,这个词字面意义上理解,它应当是一门关于个人如何参与群体决策和体现个人价值的问题。我们从小就没有机会去实践如何发表自己的意见,如何用辩论支撑自己的意见,如何让群体接受自己意见,以及如何根据群体作出的决策进行妥协和让步。可以说,谁也没有教给我们这些能力。而这种能力,又是在社会生活中一种必须的能力。如果我们接受的不是关于愚忠愚孝的而是关于如何实践民主的教育的话,那么相信社会阶层之间可以通过对话和协调的方式缓解社会压力。放到一个小团体的范围内来看,又可以通过这种方式来促进成员之间的相互交流和互信关系,从而促进社区发展。

第三,民主实践能力的缺陷,导致我们的辩论技巧只剩下了谩骂。中国国内的各大论坛,无论是技术论坛还是非技术论坛都充斥这谩骂。当一个人实在不知道用什么方式表达自己意愿的时候,就只能生产视听垃圾了。口舌之利又是似乎比生命还重要,这是在是中国人根深蒂固的毛病之一。但是,可恨之人必定有可怜之处,我们需要用仁慈的心去体会一个个贫乏的灵魂。

第四,也许文革还没有结束。看到很多人谈到过“文化大革命”对中国社会的影响,但是谁曾想过,有一种东西已经植根在很多人心中,生长在我们使用的语言里。肆意的无视规则的人身攻击和死不认错的的态度,完全体现了一种人性的恶。那件事情注定贻害万年。

Ubuntu社区是在开源社区中发展非常好的,最具代表性的是它的开发者进阶制度。我觉得从Ubuntu社区得到的很多经验,非常值得学习和参考。

嘿~,如果您喜欢我的博客,您可以通过RSS.链接将本博客的最新文章传输到您喜欢的阅读器。 Subscribe with Google 
订阅到鲜果 订阅到抓虾

亚马逊送来了新书

Posted by jcadam - 13/06/09 at 03:06 下午

PRISONER OF THE STATE
THE SECRET JOURNAL OF PREMIER ZHAO ZIYANG

因为不知道中文版在哪里买,所以我就在亚马逊订购了英语版本。一口气读了两章。正应了那句话,没有比事实更加让人难以接受的事情了。继续读下去。

嘿~,如果您喜欢我的博客,您可以通过RSS.链接将本博客的最新文章传输到您喜欢的阅读器。 Subscribe with Google 
订阅到鲜果 订阅到抓虾