UNP第13章——守护进程
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
UNP第13章——守护进程
1. 守护进程的启动⽅法
(1)系统初始化脚本启动,在系统启动阶段,按照如/etc⽬录或/etc/rc开头的⽬录中的某些脚本启动,这些守护进程⼀开始就有超级⽤户权限。
如inetd,cron,Web服务器
(2)由inetd超级服务器启动。
inetd监听⽹络请求(FTP,Telnet..),每当⼀个请求到达时,启动相应实际服务器。
(3)cron守护进程启动。
cron按照规则定期执⾏⼀些程序。
(4)at命令。
at命令指定某个时刻启动程序,这些程序由cron启动。
(5)⽤户终端启动,这需要守护进程亲⾃脱离与控制终端的关联。
2.1 syslogd守护进程
由于守护进程没有控制终端,所以⽆法向stderr输出消息,因此使⽤syslogd记录消息。
syslogd由系统初始化脚本启动,syslogd启动时完成以下步骤:
(1)读取配置⽂件,如/etc/syslog.conf以指定可能收取的消息应该如何处理。
(2)创建⼀个Unix域套接字,给它绑定到/var/run/log 或 /var/log
(3)创建⼀个UDP套接字,绑定514端⼝
(4)打开/dev/klog,从该设备获得内核的任何出错信息。
之后进⾏循环:调⽤select监听3个⽂件描述符(上⾯2,3,4步骤获得),描述符可读,则读之,并将消息写⼊⽇志。
如果收到SIGHUP则重新读取配置⽂件。
2.2 向syslogd发消息
(1)应⽤创建Unix套接字,绑定与syslogd相同的路径。
(2)创建UDP套接字,向514端⼝发信息(新的syslogd禁⽌了UDP套接字,原因是由于UDP套接字缓冲被恶意写满,导致拒绝服务攻击)
(3)调⽤syslog函数,该函数封装了使⽤Unix域套接字的⽅法。
2.3 syslog函数
void syslog(int priority, const char *format, ...);
priority由 level 和 facility 两者组合,format 是格式化串,增加了 %m(它被替换成 errno 对应的错误消息)
level 默认为LOG_NOTICE
facility 指定发送消息的进程类型
举例进程可以调⽤
syslog(LOG_INFO | LOG_LOCAL2, "rename (%s, %s) : %m", file1, file2);
/etc/syslog.conf可以这样指定
kern.* /dev/console
/var/log/cisco.log
这样来⾃ local2 设施的 info 消息就会记录到 /var/log/cisco.log
syslog ⾸次调⽤时创建Unix域套接字,后调⽤connect连接路径(/var/run/log),套接字⼀直打开,直到进程终⽌。
2.4 openlog 和 closelog
void openlog(const char *ident, int options, int facility);
void closelog(void);
openlog 在⾸次调⽤syslog前调⽤,closelog在不再发送⽇志消息时调⽤。
ident 指定 syslog每个⽇志消息⾸部的字符串,通常⽤程序名
options 参数有以下组合
facility 为没有指定设施的syslog调⽤设置⼀个默认的值,这样之后的syslog只需要指定level。
openlog 调⽤时,通常不会创建Unix域套接字,套接字直到⾸次调⽤syslog才打开。
2. daemon_init
#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()) < 0)
return (-1);
else if (pid)
_exit(0); /* parent terminates */
/* child 1 continues... */
if (setsid() < 0) /* become session leader */
return (-1);
Signal(SIGHUP, SIG_IGN);
if ( (pid = Fork()) < 0)
return (-1);
else if (pid)
_exit(0); /* child 1 terminates */
/* child 2 continues... */
daemon_proc = 1; /* for err_XXX() functions */
chdir("/"); /* change working directory */
/* close off file descriptors */
for (i = 0; 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 (0); /* success */
}
(1)fork
调⽤fork,并终⽌⽗进程,⼦进程在后台运⾏,是为了shell认为命令已经执⾏完成,从⽽命令交出控制台。
(2)setsid
创建新会话,保证⼦进程为会话头进程和进程组头进程,从⽽不再有控制终端。
(3)忽略SIGHUP,并再次fork
为了确定程序不会再获得控制终端,所以需要进程不能是会话进程的头进程。
因为如果将来进程打开控制终端,该终端会⾃动成为会话头进程的控制终端。
当会话头进程终⽌时,会向会话的所有⼦进程发送SIGHUP,所以要忽略SIGHUP。
(4)改变⼯作⽬录
守护进程需要改变⼯作⽬录到其真正的⼯作的地⽅。
原因是,守护进程可能在⽂件系统任何地⽅被启动,如果仍然在其中,那么该⽂件系统就⽆法卸载。
(5)关闭所有打开的⽂件描述符
关闭守护进程从执⾏的它的进程(通常是shell)继承来的所有打开的描述符
(6)将stdin,stdout,stderr重定向到/dev/null
这有两个好处,
保证这些常⽤描述符是打开的,针对他们的系统调⽤read返回EOF,write则由内核丢弃所有数据,因此守护进程不会因为调⽤这些程序⽽失败。
避免打开其他描述符从0,1,2开始,否则perror会将数据发到错误的地⽅。
(7)syslogd处理错误
守护进程与信号
守护进程没有控制终端,所以它不会收到SIGHUP,可以将SIGHUP作为通知信号,⽐如通知配置⽂件已经修改。
同样守护进程也不会收到SIGINT 和 SIGWINCH 信号,也可以安全的将这些信号作为通知信号。