守护进程之单实例守护进程
为了正常运行,某些守护进程实现为单实例的,也就是在任一时刻只运行该守护进程的一个副本。例如,该守护进程可能需要排它地访问一个设备。在cron守护进程情况下,如果同时有多个实例运行,那么每个副本都可能试图开始某个预定的操作,于是造成该操作的重复执行,这很可能导致出错。
如果守护进程需要访问一设备,而该设备驱动程序将阻止多次打开在/dev目录下的相应设备节点,那么这就达到了任何时刻只运行守护进程一个副本的要求。但是如果没有这种设备可供使用,那么我们就需要自行处理。
文件锁和记录锁机制是一种方法的基础,该方法用来保证一个守护进程只有一个副本在运行。如果每一个守护进程创建一个文件,并且在整个文件上加上一把写锁,那就只允许创建一把这样的写锁,所以在此之后如试图再创建一把这样的写锁就将失败,以此向后续守护进程副本指明已有一个副本正在运行。
文件锁和记录锁提供了一种方便的互斥机制。如果守护进程在整个文件上得到一把写锁,那么在该守护进程终止时,这把写锁将被自动删除。这就简化了复原所需的处理,去除了对以前的守护进程实例需要进行清理的有关操作。
实例
程序清单13-2中的函数说明了如何使用文件和记录锁以保证只运行某守护进程的一个副本。
程序清单13-2 保证只运行某个守护进程的一个副本
#include <unistd.h> #include <stdlib.h> #include <fcntl.h> #include <syslog.h> #include <string.h> #include <errno.h> #include <stdio.h> #include <sys/stat.h> #define LOCKFILE "/var/run/daemon.pid" #define LOCKMODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) extern int lockfile(int); int already_running(void) { int fd; char buf[16]; fd = open(LOCKFILE, O_RDWR|O_CREAT, LOCKMODE); if(fd < 0) { syslog(LOG_ERR, "can't open %s: %s", LOCKFILE, strerror(errno)); exit(1); } if(lockfile(fd) < 0) { if(errno == EACCES || errno == EAGAIN) { close(fd); return(1); } syslog(LOG_ERR, "can't lock %s: %s", LOCKFILE, strerror(errno)); exit(1); } ftruncate(fd, 0); sprintf(buf, "%ld", (long)getpid()); write(fd, buf, strlen(buf)+1); return(0); }
守护进程的每个副本都将试图创建一个文件,并将其进程ID写到该文件中。这使管理人员易于标识该进程。如果该文件已经加了锁,那么lockfile函数(见高级I/O之记录锁)将失败,errno设置为EACCES或EAGAIN,函数返回1,这表明该守护进程已在运行。否则将文件长度截短为0,将进程ID写入该文件,函数返回0。
我们需要将文件长度截短为0,其原因是以前守护进程实例的进程ID字符串可能长于调用此函数的当前进程的进程ID字符串。例如,若以前的守护进程的进程ID是12345,而新实例的进程ID是9999,那么将此进程ID写入文件后,在文件中留下的是99995。将文件长度截短为0就解决了此问题。
本篇博文内容摘自《UNIX环境高级编程》(第二版),仅作个人学习记录所用。关于本书可参考:http://www.apuebook.com/。