守护进程
守护进程是生存期较长的一种进程,常常在系统开始时启动,直到系统关闭时才终止。因为没有控制终端,所以是在后台运行的。
父进程ID为0的各进程通常是内核进程,它们作为系统启动过程的一部分而启动(init进程是例外,它是内核启动时的用户层进程)。内核进程是特殊的,存在于系统的整个生命周期中。init进程是进程1,是一个系统守护进程,负责启动各运行层次特定的系统服务。
守护进程的特征
大多数守护进程都是以超级用户特权运行的。没有一个守护进程有控制终端,其终端名为?,进程组ID为-1。所有用户层守护进程都是进程组的组长进程和首进程,而且是这些进程组和会话中的唯一进程。大多数守护进程的父进程是init进程。
编程规则
-
首先调用umask将文件模式创建屏蔽字设置为0,因为由继承得来的文件模式创建的屏蔽字可能会拒绝设置某些权限。
-
调用fork,然后使父进程退出:(1)如果该守护进程是作为一条简单的shell命令启动的,那么父进程终止使得shell认为这条命令已经执行完毕。(2)子进程继承了父进程的进程组ID,但具有了一个新进程的ID,这就保证了子进程不是一个进程组的组长进程,这是setsid调用的必要前提条件。
-
调用setsid创建一个新会话,使得调用进程:成为一个新会话的首进程,成为一个新进程组的组长进程,没有控制终端。
-
将当前工作目录改为根目录。防止当前继承的工作目录在一个装配文件系统中。
-
关闭不再需要的文件描述符。
void create_daemon()
{
pid_t pid;
pid = fork();
if(pid == -1)
{
syslog(LOG_ERR,"fork error\n");
exit(1);
}
else if(pid)
{
exit(0);
}
if(setsid() == -1)
{
syslog(LOG_ERR,"setsid error\n");
exit(1);
}
else if(pid)
{
exit(0);
}
chdir("/");
close(0);
close(1);
close(2);
umask(0);
return;
}
出错记录
大多数用户进程(守护进程)调用syslog函数以产生日志消息。
void openlog();
void syslog();
void closelog();
int setlogmask();
如果不调用openlog,那么在第一次调用syslog时,会自动调用openlog
单实例守护进程
在任一时刻,只运行该守护进程的一个副本。文件锁和记录锁机制是实现的一种方法,用于保证一个守护进程只有一个副本在运行。如果每一个守护进程创建一个文件,并且在整个文件上加上一把写锁,那就只允许创建一把这样的写锁。
extern int lockfile(int);
....
if(lockfile(fd) < 0)
{
if(errno == EACCES || errno == EAGAIN)
{
close(fd);
return(1);
}
syslog(LOG_ERR,"can't lock %s",LOCKFILE);
exit(1);
}
守护进程的惯例:
-
若守护进程使用锁文件,那么该文件通常存放在/var/run/目录中,锁的名字为:守护进程名/服务名.pid
-
若守护进程支持配置选项,那么配置文件通常放在/etc目录中。配置文件的名字为:守护进程名/服务名.conf。
-
守护进程可由命令行启动,通常是由系统初始化脚本(/etc/rc* 或者 /etc/init.d/*)启动的。
-
守护进程捕捉SIGHUP信号,当它们接收到该信号时,重读配置文件。