Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2416575
  • 博文数量: 392
  • 博客积分: 7040
  • 博客等级: 少将
  • 技术积分: 4138
  • 用 户 组: 普通用户
  • 注册时间: 2009-06-17 13:03
个人简介

范德萨发而为

文章分类

全部博文(392)

文章存档

2017年(5)

2016年(19)

2015年(34)

2014年(14)

2013年(47)

2012年(40)

2011年(51)

2010年(137)

2009年(45)

分类:

2010-09-27 10:37:25

Daemon编程与启动

曹新宇


背景知识
当我们登录进入UNIX操作系统时,系统为我们产生一个登录shell, 登录shell的标准输
入、输出和错误输出指向一个终端文件。在登录shell中,假如我们键入如下命令:
$proc1 | proc2 &$proc3 | proc4 | proc5这两个命令产生的进程组和登录shell这一
进程组的集合称作一个会话(session),登录shell被称作会话的会话头(session leader),与
登录shell相连的终端文件被称作控制终端(control terminal)。一个会话最多有一个控制
终端,也可能没有控制终端。有控制终端的会话的会话头被称作控制进程(control proces)
。一个会话中的进程组可分为前台进程组和后台进程组,当会话有控制终端时,则它有一个前
台进程组,会话中的其他进程组都是后台进程组。只有前台进程组可以接收终端输入,当我们
键入终端的控制键时,如中断键(一般为control-c)或者退出键(一般为control-\),这会引起
中断信号SIGINT或者退出信号SIGQUIT发送给前台进程组中的所有进程。当终端挂起时,IGH
UP信号发送到会话中的每一个进程,不管是前台进程组,还是后台进程组(这一关系如下图所
示)。
@@36213000.GIF;图1@@
一个进程可以调用setsid函数建立一个新的会话,如果调用进程不是进程组头,这个函数
产生一个新的会话,同时发生下面三件事:
·这个进程变为新会话的会话头,并且是会话中的唯一进程。
·这个进程变为新进程组的进程组头,新进程ID是调用进程的ID。
·进程没有控制终端,如果调用setsid之前进程拥有控制终端,调用后不再拥有。
如果调用进程是进程组头,则该函数调用失败。

什么是Daemon
daemon的意思是精灵或恶魔,在UNIX中一般翻译为守护或驻留进程。除非意外情况或人
为干预,daemon一般在操作系统启动时运行,关机时结束。daemon通常作为客户 /服务器模
式中的服务器进程。UNIX操作系统中有大量的daemon日复一日地为客户服务。其主要特点是
:daemon是一种后台进程,没有控制终端,因此不受终端控制键的影响,也不会由于中断挂起而
退出。而且它是进程组头和会话头,是进程组和会话中唯一的进程。
daemon编码基本原则如下:
(1)调用fork并且结束父进程。这样做可以达到两个目的。首先,如果daemon是在shel命
令中启动的,结束父进程会使shell认为命令已经结束。第二,子进程继承了父进程的进程组
ID同时得到一个新的进程ID,所以我们可以保证子进程不是进程组头,这是调用setsid函数的
先决条件。
(2)调用setsid产生一个新的会话。进程变为新会话的会话头和新进程组的进程组头,同
时进程没有控制终端。以上这些满足了daemon的特点。也就是说,进程已经基本成为daemo进
程。但是还有其他一些工作要做。
(3)关闭所有打开的文件描述字。
(4)改变当前的工作目录。
(5)重设文件存取建立的屏蔽码。
(6)忽略SIGCLD信号。作为server,当daemon接收到client的请求时,一般fork一个子进
程为client服务。当子进程退出时,如果daemon未调用wait等待子进程结束,则子进程成为o
mbie进程;对每一个zombie进程,内核会保留一些有关的信息,如进程ID,以便父进程调用wit
。如果有大量client请求服务,必将产生很多zombie进程占用系统内存,忽略SIGCLD信号则可
避免zombie进程的积累。
下面是使用前面的编码原则编写的简单的daemon进程。
#include
#include
#include
int daemon_init(void){
pid_t pid;
if ( (pid = fork()) < 0)
return(-1);
else if (pid != 0)
exit(0); /* 父进程退出 */
/* 子进程继续 */
setsid(); /* 变为会话头 */
chdir("/"); /* 改变工作目录 */
umask(0); /* 重设文件存取建立的屏蔽码 */
signal(SIGCLD,SIG_IGN); /* 忽略SIGCLD信号 */
return(0);
}

Daemon日志
守护进程没有控制终端,所以当daemon希望处理错误信息时,我们不能使用printf函数向
终端写错误信息。当然,daemon可以打开一个文件,在此文件中记录错误信息。这样的文件应
该以添加方式打开,以便前边的信息不被后边的信息所覆盖。但使用这种方法存在一些问题
:随着日志信息的增多,系统管理员必须不断地对该文件进行检查,使之保持在一定长度范围
内,这对系统管理员是一个比较麻烦的事情。此外,如果我们想使用其他文件记录日志时,必
须重新修改daemon源文件。因此,在UNIX系统中提供了syslogd daemon专门用于记录日志信
息,使用syslogd daemon可以使我们灵活安排日志使用的文件。
syslogd daemon在启动时读配置文件/etc/syslog.conf决定不同的信息分别存放在哪
个文件中,例如,可以指定紧急信息直接送往控制台,而一般的告警事件记录在某一文件中。
使用syslogd的编程接口是:
#include
void openlog(char *ident,int option,int facility);
void syslog(int priority,char *format,……);
void closelog(void);
在这些系统调用中,我们可以指定信息的来源、信息的重要程度,可以在信息上附加其
他信息。这些接口的具体使用方法和配置文件syslog.conf的格式,可参考系统提供的手册。
Daemon的重配置
daemon在启动时首先读配置文件对daemon中使用的参数进行配置。当我们想改变某些参
数时,我们可以编辑配置文件。当然,编辑好配置文件后并不能立刻改变了aemon中的参数,d
aemon必须重新读配置文件。一种方法是重新启动系统,当然这种方法是一种最笨的方法。好
一点的方法是使用ps命令找出daemon进程ID,结束该进程然后重新运行demon。这种办法的缺
点是:如果某一进程正在与daemon通信时,会使该进程感觉到。最好的办法是:daemon收到某
个事先约定的信号时,重新读配置文件,这样就不必中断daemon正常运行。
这样的daemon程序如下所示:
void read_config_file()
{
int fd;
fd=open("config_file",O_RDONLY);
/*读配置文件并配置参数*/
close(fd);
signal(SIGHUP,read_config_file);
}
main()
{
read_config_file();
/*进入服务循环*/
while(1)
{
}
}
需要注意的是:在read_config_file中,最好不要使用fopen,fread等函数,因为它们是不
可重入函数(nonreentarnt function),否则可能产生无法预测的结果。
假如我们想重新配置syslogd daemon参数,编辑好syslog.conf文件后,可利用如下的命
令,对syslogd进行配置:
$ps -ae | grep syslogd /* 查出syslogd daemon的ID */$kill -HUP processID* 强
迫daemon重新读配置文件并配置 */

Daemon的运行
一般情况下,daemon在系统启动时自动启动,要使daemon随系统的启动而启动,必须在相
应的启动运行脚本中加入一些内容。当UNX系统启动时,内核产生一个init进程,它的进程ID
为1,由init进程执行daemon启动脚本。
在BSD中,init进程执行/etc/rc.boot、/etc/rc和/etc/rc.local脚本。rc.local脚本是
存放运行本地daemon命令的地方。这些命令是简单的B shell命令。因此启动daemon只需在
rc.local中加入运行该daemon的命令即可。
在SVR4中情况比较复杂。init进程支持多个运行级别,从0到6和s(单用户)。所谓运行级
别可以看作是系统的软件配置。每种运行级别选择运行不同的进程。运行级别不同时,ini所
采取的动作也不同,这由配置文件和多个shell脚本文件控制。其中配置文件是/etc/initab
文件。下面是该文件的一个例子:
init:2:initdefault:
stty::sysinit:stty 9600 clocal icanon echo opost onlcr ienqak ixon icrnl in
par link::wait:/bin/sh -c "rm -f /dev/syscon; ln /dev/systty /dev/syscon" >/dev
/console 2>&1
rc ::wait:/etc/rc /dev/console 2>&1 # system initializatio
halt:6:wait:/usr/lib/X11/ignition/shutdown.ksh # NOTE: runlevel 6 is reserved for system shutdown.
vue :234:respawn:/etc/vuerc #VUE validation and invocation
inittab文件的格式是:
标记::运行级别:动作:进程每行的结尾由回车键结束。#后是对该行的注释。每行中的
各段由冒号隔开,各段的意义如下:
·标记:用于标记这一行。
·运行级别:每行所处的运行级别。例如,如果init的运行级别是1,则它将处理标有1的
一行内容。当该段空时,则任何级别都需运行该行命令。
·动作:告诉init管理该daemon时应该采取的策略。最普通的策略是wait,即该daemon只
在系统启动时被运行,不管以后是否中断。respawn的意思是:系统监视daemon的运行状况,当
它中断时自动重新启动。还有其他的策略值这里就不一一介绍了。
·进程:被执行的B shell脚本或shell命令。
我们可以直接在inittab文件中写入daemon启动命令,或者写在B shell脚本中。
阅读(2449) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~