在UNIX系统中,守护进程遵循下列通用惯例。
- 若守护进程使用锁文件,那么该文件通常存储在
/var/run
目录中。然而需要注意的是,守护进程可能需要具有超级用户权限才能在此目录下创建文件。锁文件的名字通常是name.pid
,其中,name
是该守护进程或服务的名字。例如,cron
守护进程锁文件的名字是/var/run/crond.pid
。 - 若守护进程支持配置选项,那么配置文件通常存放在
/etc
目录中。配置文件的名字通常是name.conf
,其中,name
是该守护进程或服务的名字。例如,syslogd
守护进程的配置文件通常是/etc/syslog.conf
。 - 守护进程可用命令行启动,但通常它们是由系统初始化脚本之一(
/etc/rc*
或/etc/init.d/*
)启动的。如果在守护进程终止时,应当自动地重新启动它,则我们可在/etc/inittab
中为该守护进程包括respawn
记录项,这样,init就将重新启动该守护进程。(假定系统使用System V风格的init命令。) - 若一个守护进程有一个配置文件,那么当该守护进程启动时会读该文件,但在此之后一般就不会再查看它。若某个管理员更改了配置文件,那么该守护进程可能需要被停止,然后再启动,以使配置文件的更改生效。为避免此种麻烦,某些守护进程将捕捉
SIGHUP
信号,当它们接收到该信号时,重新读配置文件。因为守护进程并不与终端相结合,它们或者是无控制终端的会话首进程,或者是孤儿进程组的成员,所以守护进程没有理由期望接收SIGHUP
。于是,守护进程可以安全地重复使用SIGHUP
。
实例
图13-7所示的程序说明了守护进程可以重读其配置文件的一种方法。该程序使用sigwait
以及多线程,对此我们已经在12.8节讨论过。
#include "apue.h"
#include <pthread.h>
#include <syslog.h>
sigset_t mask;
extern int already_running(void);
void
reread(void)
{
/* ... */
}
void *
thr_fn(void *arg)
{
int err, signo;
for (;;) {
err = sigwait(&mask, &signo);
if (err != 0) {
syslog(LOG_ERR, "sigwait failed");
exit(1);
}
switch (signo) {
case SIGHUP:
syslog(LOG_INFO, "Re-reading configuration file");
reread();
break;
case SIGTERM:
syslog(LOG_INFO, "got SIGTERM; exiting");
exit(0);
default:
syslog(LOG_INFO, "unexpected signal %d\n", signo);
}
}
return(0);
}
int
main(int argc, char *argv[])
{
int err;
pthread_t tid;
char *cmd;
struct sigaction sa;
if ((cmd = strrchr(argv[0], '/')) == NULL)
cmd = argv[0];
else
cmd++;
/*
* Become a daemon.
*/
daemonize(cmd);
/*
* Make sure only one copy of the daemon is running.
*/
if (already_running()) {
syslog(LOG_ERR, "daemon already running");
exit(1);
}
/*
* Restore SIGHUP default and block all signals.
*/
sa.sa_handler = SIG_DFL;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
if (sigaction(SIGHUP, &sa, NULL) < 0)
err_quit("%s: can't restore SIGHUP default");
sigfillset(&mask);
if ((err = pthread_sigmask(SIG_BLOCK, &mask, NULL)) != 0)
err_exit(err, "SIG_BLOCK error");
/*
* Create a thread to handle SIGHUP and SIGTERM.
*/
err = pthread_create(&tid, NULL, thr_fn, 0);
if (err != 0)
err_exit(err, "can't create thread");
/*
* Proceed with the rest of the daemon.
*/
/* ... */
exit(0);
}
图13-7 守护进程重读配置文件
该程序调用了图13-1中的daemonize
来初始化守护进程。从该函数返回后,调用图13-6中的already_running
函数以确保该守护进程只有一个副本在运行。到达这一点时,SIGHUP
信号仍被忽略,所以需恢复对该信号的系统默认处理方式;否则调用sigwait
的线程决不会见到该信号。
实例
并非所有守护进程都是多线程的。图 13-8 中的程序说明一个单线程守护进程如何捕捉SIGHUP
并重读其配置文件。
#include "apue.h"
#include <syslog.h>
#include <errno.h>
extern int lockfile(int);
extern int already_running(void);
void
reread(void)
{
/* ... */
}
void
sigterm(int signo)
{
syslog(LOG_INFO, "got SIGTERM; exiting");
exit(0);
}
void
sighup(int signo)
{
syslog(LOG_INFO, "Re-reading configuration file");
reread();
}
int
main(int argc, char *argv[])
{
char *cmd;
struct sigaction sa;
if ((cmd = strrchr(argv[0], '/')) == NULL)
cmd = argv[0];
else
cmd++;
/*
* Become a daemon.
*/
daemonize(cmd);
/*
* Make sure only one copy of the daemon is running.
*/
if (already_running()) {
syslog(LOG_ERR, "daemon already running");
exit(1);
}
/*
* Handle signals of interest.
*/
sa.sa_handler = sigterm;
sigemptyset(&sa.sa_mask);
sigaddset(&sa.sa_mask, SIGHUP);
sa.sa_flags = 0;
if (sigaction(SIGTERM, &sa, NULL) < 0) {
syslog(LOG_ERR, "can't catch SIGTERM: %s", strerror(errno));
exit(1);
}
sa.sa_handler = sighup;
sigemptyset(&sa.sa_mask);
sigaddset(&sa.sa_mask, SIGTERM);
sa.sa_flags = 0;
if (sigaction(SIGHUP, &sa, NULL) < 0) {
syslog(LOG_ERR, "can't catch SIGHUP: %s", strerror(errno));
exit(1);
}
/*
* Proceed with the rest of the daemon.
*/
/* ... */
exit(0);
}
在初始化守护进程后,我们为SIGHUP
和SIGTERM
配置了信号处理程序。可以将重读逻辑放在信号处理程序中,也可以只在信号处理程序中设置一个标志,并由守护进程的主线程完成所有的工作。