Perl创建守护进程之二
前面这篇文章简单地阐述了用Perl创建守护进程(daemon)的方法。 实际上,创建一个多进程的守护进程需要注意很多事情。 下面的内容来自著名的Unix Programming FAQ的1.7节,介绍了Unix下创建守护进程的方法。
通常将一个不与任何终端相关联的后台进程定义为daemon进程。下面是通常所需的七 个步骤:
a. fork()之后父进程退出。子进程确保不是process group leader,这是成功调用 setsid()所要求的。
b. setsid(),创建新的session和process group,成为其leader,并脱离控制终端。
c. 再次fork()之后父进程退出,子进程确保不是session leader,将永远不会重获 控制终端。这是SVR4的特性所致。
d. chdir( “/” ),减少管理员卸载(unmount)文件系统时可能遇上的麻烦。这一步可 选,也可chdir()到其它目录。
e. umask( 0 ),使当前进程对自己所写文件拥有完全控制权,避免继承的umask()设 置带来困挠。这一步可选。
f. 关闭0、1、2三个句柄。许多daemon程序用sysconf()获取_SC_OPEN_MAX,并在一 个偱环中关闭所有可能打开的文件句柄。目的在于释放不必要的系统资源,它们 是有限资源。
g. 出于安全以及健壮性考虑,即使当前进程不使用stdin、stdout、stderr,也应重 新打开0、1、2三个句柄,使之对应/dev/null。当然,你也可以根据需要使之对 应不同的(伪)文件。总之,保持0、1、2三个句柄呈打开状态,并使之指向无害文 件。
实际上,除了以上7点之外你还应当做两件事情。第一件就是切换到daemon用户——避免由于代码的安全漏洞导致root权限泄漏; 第二件事情就是忽略 SIGCHLD 信号,让 init 进程来回收结束了的子进程。
如果将上述内容用 perl 写出来就是下面的程序:
#!/usr/bin/perl
#------------------------------
# daemon process sample, written by charlee, 2006/10/12
#
use strict;
use POSIX 'setsid';
our $USER = 'lijian';
our $GROUP = 'lijian';
#------------------------------
# child process to be created by daemon
sub child-process {
# do some work here
}
#------------------------------
# create daemon process
# 1. fork() so the parent can exit, returns control to shell.
exit if fork;
# 2. setsid() to become a process group and session group leader.
&setsid();
# 3. fork() again so the parent (session group leader) can exit.
exit if fork;
# 4. (OPTIONAL) chdir('/') to ensure our daemon doesn't keep any directory in use.
chdir '/';
# 5. (OPTIONAL) umask() so we have complete control over the permissions of anything we write.
umask 022;
# 6. close() fds 0, 1, and 2.
close STDIN;
close STDOUT;
close STDERR;
# 7. redirect fds 0, 1, and 2 to /dev/null
open STDIN, '/dev/null';
open STDOUT, '>/dev/null';
open STDERR, '>/dev/null';
# 8. change to daemon user and group.
&sudo($USER, $GROUP);
# 9. ignore SIGCHLD signal to avoid zombie processes
$SIG{CHLD} = 'IGNORE';
# 10. start main loop.
while(1) {
my $pid = fork;
if ($pid == 0) {
&child_process();
exit;
} else {
sleep 1;
}
}
# main end
# function to change user and group
sub sudo {
my ($user, $group) = @_;
my $uid = (getpwnam($user))[2];
my $gid = (getgrnam($group))[2];
($(, $)) = ($gid, "$gid $gid");
($<, $>) = ($uid, $uid);
}
__END__