由于守护进程在后台运行,为系统或用户提供某种服务,因此通常只需要一个运行实例就可以了,而且在大部分情况下只能有一个实例在运行。例如cron进程,若有多个实例在运行,那么各个实例都会根据crontab执行一份用户指定的任务,岂不是乱了套了?还有其他很多守护进程是设备相关的,而这些设备有很可能是非共享的,所以这样的守护进程也不能运行多个。
文件锁和记录锁机制是一种实现守护进程单例运行的方法。如果每一个守护进程创建一个文件,并且在整个文件上加上一把锁,那就只允许创建一把这样的写锁,所以在此之后试图再创建一把这样的写锁就会失败,以此向后续守护进程的副本指明已经存在一个正在运行的副本了。守护进程终止时,这把锁将被自动删除。
int lockfile(int fd) //~ try to lock the file, affecting errno when it fails. { struct flock fk; fk.l_type = F_WRLCK; fk.l_start = 0; fk.l_whence = SEEK_SET; fk.l_len = 0; return (fcntl(fd, F_SETLK, &fk)); } int already_running(const char* fname) //~ return 1, if another daemon is running { int fd; fd = open(fname, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); if( fd < 0 ) { //~ in daemon process, you should use syslog, instead of stdout, for logging //~ syslog(LOG_ERR, "cannot open %s: %s", fname, strerror(errno)); fprintf(stdout, "cannot open %s: %s", fname, strerror(errno)); fflush(stdout); exit(1); } if( lockfile(fd) < 0 ) { if( errno == EACCES || errno == EAGAIN ) { //~ syslog(LOG_ERR, "cannot lock %s: %s", fname, strerror(errno)); fprintf(stdout, "cannot lock %s: %s", fname, strerror(errno)); close(fd); return 1; } //~ syslog(LOG_ERR, "cannot lock %s: %s", fname, strerror(errno)); fprintf(stdout, "cannot lock %s: %s", fname, strerror(errno)); fflush(stdout); exit(1); } ftruncate(fd, 0); char buf[16]; sprintf(buf, "%ld", (long)getpid()); write(fd, buf, strlen(buf) + 1); return 0; }