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的更多相关文章
随机推荐
- 一个方便的java分页算法
一个好用的java分页算法,代码如下,只需要分页参数继承Pageable类就可以很方便分页了 package cn.com.base.common.pagination; /** * 分页基类 * * ...
- 80% UI 初学者走过的弯路,你走了几条?
关于UI 对于初学UI设计的人而言,可能对UI具体是做什么,或者自己是否能顺利转行胜任这样的岗位存在一定的顾虑,今天我们就来重点说说UI是做什么的,以及学UI到有哪些需要避免的弯路. 1.UI设计是做 ...
- RSA生成、加密、解密、签名。
首先,要会生成RSA密码对. https://app.alipay.com/market/document.htm?name=saomazhifu#page-23 (事例中的密钥对好像有问题,最 ...
- source Insight工程的简单使用
本文以管理虚拟机里面的uboot为例: 1.选择project->New project->选择工程路径,假设为D:\uboot:->project has its own conf ...
- XE7 里面添加自定义View
经过xe4,xe5,xe6 这么几个版本的磨合,易博龙终于在今年9月推出了统一的多平台开发版本-XE7. 经过最近几天的测试,非常不错.如果各位同学在做移动开发,强烈建议使用XE7. 前面几个版本可以 ...
- 前端之css语法3
一 float属性 1 基本的浮动规则: block元素和inline元素在文档流中的排列方式. block元素通常被现实独立的一块,独占一行.多个block元素会各自新起一行,默认block预算宽度 ...
- 2018.07.04 BZOJ 2823: AHOI2012信号塔(最小圆覆盖)
2823: [AHOI2012]信号塔 Time Limit: 10 Sec Memory Limit: 128 MB Description 在野外训练中,为了确保每位参加集训的成员安全,实时的掌握 ...
- 2018.06.30 BZOJ1026: [SCOI2009]windy数(数位dp)
1026: [SCOI2009]windy数 Time Limit: 1 Sec Memory Limit: 162 MB Description windy定义了一种windy数.不含前导零且相邻两 ...
- String、StringBuffer与StringBuilder之间区别(转)
最近学习到StringBuffer,心中有好些疑问,搜索了一些关于String,StringBuffer,StringBuilder的东西,现在整理一下. 关于这三个类在字符串处理中的位置不言而喻,那 ...
- cxf-rs 和 swagger 的点
1. 从web.xml 说起 <servlet> <servlet-name>CXFServlet</servlet-name> <servlet-class ...