摘自 UNP

 #include "unp.h"
#include <syslog.h> #define MAXFD 64 extern int daemon_proc; // defined in error.c int daemon_init(const char* pname, int facility)
{
int i;
pid_t pid; if ( (pid = fork()) < )
return -;
else if (pid)
_exit(); // parent terminates // child 1 continue... if (setsid() < ) // become session leader
return -; signal(SIGHUP, SIG_IGN);
if ( (pid = fork()) < )
return -;
else if (pid)
_exit(); // child 1 terminates // child 2 continue... daemon_proc = ; // for err_XXX() functions chdir("/"); // change working directory // close off file descriptors
for (i=; i<MAXFD; ++i)
close(i); // redirect stdin, stdout, and stderr to /dev/null
open("/dev/null", O_RDONLY);
open("/dev/null", O_RDWR);
open("/dev/null", O_RDWR); openlog(pname, LOG_PID, facility); return ; // success
}

fork

13~16  首先调用 fork,然后终止父进程,留下子进程继续运行。如果本进程是从前台作为一个 shell 命令启动的,当父进程终止时,shell 就认为该命令已执行完毕。这样子进程就自动在后台运行。另外,子进程继承了父进程的进程组 ID,不过它有自己的进程 ID。这就保证子进程不是一个进程组的头进程,这是接下去调用 setsid 的必要条件。

setsid

20~21  setsid 是一个 POSIX 函数,用于创建一个新的会话(session)。当前进程变为新会话的会话头进程以及新进程组的进程组头进程,从而不再有控制终端。

忽略 SIGHUP 信号并再次 fork

23~27  忽略 SIGHUP 信号并再次调用 fork。该函数返回时,父进程实际上是上一次调用 fork 产生的子进程,它被终止掉,留下新的子进程继续运行。再次 fork 的目的是确保本守护进程将来即使打开了一个终端设备,也不会自动获得控制终端。当没有控制终端的一个会话头进程打开一个终端设备时(该终端不会是当前某个其他会话的控制终端),该终端自动成为这个会话头进程的控制终端。然而再次调用 fork 之后,我们确保新的子进程不再是一个会话头进程,从而不能自动获得一个控制终端。这里必须忽略 SIGHUP 信号,因为当会话头进程(即首次 fork 产生的子进程)终止时,其会话中的所有进程(即再次 fork 产生的子进程)都收到 SIGHUP 信号。

为错误处理函数设置标识

31    把全局变量 daemon_proc 置为非 0 值。这个外部变量由我们的 err_XXX 函数定义,其值非 0 是在告知它们改为调用 syslog,以取代 fprintf 到标准错误输出。该变量省得我们从头到尾修改程序代码,在服务器不是作为守护进程运行的场合(例如测试服务器程序时)调用某个错误处理函数,在服务器作为守护进程运行的场合调用 syslog。

改变工作目录

33    把工作目录改到根目录,不过有些守护进程另有原因需改到其他目录。举例来说,打印机守护进程可能改到打印机的假脱机处理(spool)目录,因为那里是它做全部工作的地方。要是守护进程产生了某个 core 文件,该文件就存放在当前工作目录中。改变工作目录的另一个理由是,守护进程可能是在某个任意的文件系统中启动,如果仍然在其中,那么该文件系统就无法拆卸(unmounting),除非使用潜在破坏性的强制措施。

关闭所有打开的描述符

36~37  关闭本守护进程从执行它的进程(通常是一个 shell)继承来的所有打开着的描述符。问题是怎样监测正在使用的最大描述符:没有现成的 Unix 函数提供该值。监测当前进程能够打开的最大描述符数目自有办法,然而由于这个限制可以是无限的,这样的监测也变得复杂起来。我们的解决办法是干脆关闭前 64 个描述符,即使其中大部分可能并没有打开。

    Solaris 提供了一个名为 closefrom 的函数,可以用于解决守护进程的这个问题。

将 stdin、stdout 和 stderr 重定向到 /dev/null

40~42  打开 /dev/null 作为本守护进程的标准输入、标准输出和标准错误输出。这一点保证这些常用描述符是打开的,针对它们的 read 系统调用返回 0(EOF),write 系统调用则由内核丢弃所写数据。打开这些描述符的理由在于,守护进程调用的那些假设能从标准输入读或者往标准输出或标准错误输出写的库函数将不会因为这些描述符未打开而失败。这种失败是一种隐患。要是一个守护进程未打开这些描述符,却作为服务器打开了与某个客户关联的一个套接字,那么这个套接字很可能占用这些描述符(譬如标准输出或标准错误输出的描述符 1 或 2),这种情况下如果守护进程调用诸如 perror 之类函数,那就会把非预期的数据发送给那个客户。

使用 syslog 处理错误

44    调用 openlog。其中第一个参数来自调用者,通常是程序的名字(譬如 argv[0])。第二个参数指定把进程 ID 加到每个日志消息中。第三个参数同样由调用者指定,其值为图13-2所示的常值之一或为0(如果默认值 LOG_USER 可以接受的话)。

    我们指出,既然守护进程在没有控制终端的环境下运行,它绝不会收到来自内核的 SIGHUP 信号。许多守护进程因此把这个信号作为来自系统管理员的一个通知,表示其配置文件已发生改动,守护进程应该重新读入其配置文件。守护进程同样绝不会收到来自内核的 SIGINT 信号和 SIGWINCH 信号,因此这些信号也可以安全地用作系统管理员的通知手段,指示守护进程应该做出反应的某种变动已经发生。

daemon_int的更多相关文章

随机推荐

  1. Python发送邮件不需要发件人密码认证

    #!/usr/bin/python # coding: UTF-8 import smtplib from email.mime.text import MIMEText receivers_list ...

  2. 关于前一篇innodb自增列自己的一点补充

    上篇文章是我转载的,忘记注明了出处,在这里深感歉意.但是上篇文章中关于自增列预留ID的计算我当时怎么弄明白,后来自己想了想终于想通了,在这里详细解释一下. 我们以一次性插入10行为例,表格如下:  插 ...

  3. sdk接入

    文档 http://blog.csdn.net/chenjie19891104/article/details/42217281 视频: https://chuanke.baidu.com/v2869 ...

  4. 13 Maven 编写插件

    Maven 编写插件 Maven 的任何行为都是由插件完成的,包括项目的清理.绵编译.测试以及打包等操作都有其对应的 Maven 插件.每个插件拥有一个或者多个目标,用户可以直接从命令行运行这些插件目 ...

  5. hadoop 学习(一)ubuntu14.04 hadoop 安装

    1.创建用户组 sudo addgroup hadoop 2.创建用户 sudo adduser -ingroup hadoop hadoop 回车之后会提示输入密码,输入自己要设定的密码然后一路回车 ...

  6. 使用java做paypal开发时购买东西支付不成功的原因

    使用java做paypal开发时购买东西支付不成功的原因 没有设置网站习惯设定,登陆自己的paypal账户,在网站习惯设定上填写回调的url路径,这样就可以 支付成功了并且异步修改订单的状态. 支付成 ...

  7. 2018.09.20 atcoder Painting Graphs with AtCoDeer(tarjan+polya)

    传送门 一道思维题. 如果没有环那么对答案有k的贡献. 如果恰为一个环,可以用polya求贡献. 如果是一个有多个环重叠的双联通的话,直接转化为组合数问题(可以证明只要每种颜色被选取的次数相同一定可以 ...

  8. Part 3 - Advanced Concepts(11-13)

    https://simpleisbetterthancomplex.com/series/2017/09/18/a-complete-beginners-guide-to-django-part-3. ...

  9. php读取用友u8采购入库单列表及详细

    <?php class erpData { protected static $erp; public function __construct() { $dbhost ="192.1 ...

  10. String 简介

    特性: 1.字符串的不可改变性 String  s="a"+"b"+"c"; 创建了四个对象 在方法常量池中存储   a“,”b“,”c“, ...