daemon_int
摘自 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的更多相关文章
随机推荐
- idea 注释文件和方法注释
类注释: 如下图所示
- 深入浅出 JMS(四) - ActiveMQ 消息存储
深入浅出 JMS(四) - ActiveMQ 消息存储 一.消息的存储方式 ActiveMQ 支持 JMS 规范中的持久化消息与非持久化消息 持久化消息通常用于不管是否消费者在线,它们都会保证消息会被 ...
- 子查询 in 潜在的问题 - 建议最好别用
转至:http://wiki.lessthandot.com/index.php/Subquery_typo_with_using_in Subquery typo with using in Fro ...
- 解决启动nginx时报80端口被占用的问题
如何解决启动nginx时报80端口被占用 最近公司的的一个服务器上需要部署多个项目,但80端口只有一个,所有只有使用Nginx来代理,当访问域名时就可以自动 转到IP:端口号,而不需要在域名后面加端口 ...
- 2018.10.09 NOIP模拟 路途(递推+矩阵快速幂优化)
传送门 签到题.(考试的时候写挂爆0) 令AiA_iAi表示邻接矩阵的iii次幂. 于是就是求Al+Al+1+...+ArA_l+A_{l+1}+...+A_rAl+Al+1+...+Ar. ...
- 2018.09.08 poj1185 炮兵阵地(状压dp)
传送门 状压dp经典题. 我们把每一行的状态压成01串. 预处理出每一行可能出现的状态,然后转移每个被压缩的状态的1的个数就行了. 注意当前行转移要考虑前两行的状态. 还要注意只有一行的情况. 代码: ...
- java报错java/lang/NoClassDefFoundError: java/lang/Object
安装完java出错 javac和java -version 都无效,报错如上 解决方法,更改文件中的两个文件(前提是你的 vim /etc/profile 文件路径写的正确) /usr/java/ ...
- Android热补丁技术—dexposed原理简析(阿里Hao)
本文由嵌入式企鹅圈原创团队成员.阿里资深工程师Hao分享. 上篇文章<Android无线开发的几种常用技术>我们介绍了几种android移动应用开发中的常用技术,其中的热补丁正在被越来越多 ...
- html监听,键盘事件
<script type="text/javascript" language=JavaScript charset="UTF-8"> v ...
- 自适应XAML布局经验总结 (二) 局部布局设计模式1
本系列对实际项目中的XAML布局场景进行总结,给出了较优化的自适应布局解决方案,希望对大家有所帮助. 下面开始介绍局部布局设计模式. 1. 工具栏模式 适用于工具栏,标题等的布局. 此块布局区域外层使 ...