拾遗:Unix 守护进程编写规范

时间:2022-06-01 15:23:42
//标准库自带函数,通常以 daemon(0, 0) 方式调用
int daemon(int nochdir, int noclose) Linux:
  #include <unistd.h> FreeBSD:
  #include <stdlib.h>

//自主实现原理如下

[a] 编写守护进程的要点

  • 指定 umask 值,由环境继承来的默认权限有可能存在问题
  • 使该守护进程成为一个孤儿进程组中的唯一成员:
    • fork 之后终止父进程,此举确保不是进程组首进程,然后 setsid 创建新会话
    • 丢弃会话首进程身份(再次 fork),从而确保了该进程不会获得控制终端
  • 切换到正确的工作目录中
  • 如果没有关联的配置文件,则可以设置 SIG_IGN 忽略 SIG_HUP 信号
  • 关闭不需要的文件描术符,可以将常用的 0、1、2 三个描述符关联至 /dev/null,以防止意外 I/O

[b] 样例

 #include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/resource.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h> void daemonize(const char *);
char *workdir = "/"; int
main(void)
{
daemonize(workdir);
while()
{
sleep();
}
} void
daemonize(const char *workdir)
{
pid_t pid;
int fd0, fd1, fd2;
struct rlimit rl;
struct sigaction sa; umask();
chdir(workdir); pid = fork();
if (pid < )
{
perror("fork");
}
else if (pid > )
{
_exit();
} setsid(); pid = fork();
if (pid < )
{
perror("fork");
}
else if (pid > )
{
_exit();
} if (- == getrlimit(RLIMIT_NOFILE, &rl))
{
perror("getrlimit");
}
if (rl.rlim_max == RLIM_INFINITY)
{
rl.rlim_max = ;
}
for(pid = ; pid < rl.rlim_max; ++pid)
{
close(pid);
} fd0 = open("/dev/null", O_RDWR);
fd1 = dup2(fd0, );
fd2 = dup2(fd0, ); sa.sa_handler = SIG_IGN;
sa.sa_flags = ;
sigemptyset(&sa.sa_mask);
if (sigaction(SIGHUP, &sa, NULL) < )
{
perror("sigaction");
}
}

...