小公司研发总监,既当司令也当兵!
分类: LINUX
2016-10-09 14:02:58
在开发中,往往希望一个应用程序在一台设备上只启动一个实例;但有时候,应用程序可能由其他程序、脚本启动,难免会重复执行,导致启动多个实例。刚刚在项目中就遇到一个:一个进程可能会开机启动,也可以由一个网络状态变更脚本触发启动;大多数情况下没有问题,但在个别特例网络环境中,可能会反复调度脚本,导致该程序多次启动,相互干扰导致运行异常。
为了避免这个问题,对启动脚本做了修改:在shell脚本中,启动该进程时,记录一个pid文件,如果该脚本被多次调度,那么通过判断pid文件的存在来避免重复启动进程。
上面的方法多少情况下简单有效,但如果多个脚本都可以启动,那么维护脚本就变得复杂。有没有方法在进程内部进行判断,来避免重复启动呢?答案是肯定的。接下来我们介绍一种采用文件锁的方式来避免进程启动多个实例。
思路:借鉴上面shell采用pid文件的方式,我们通过生成一个pid文件,并且采用write lock锁住该文件;如果有重复启动的情况,后面的进程再尝试锁住该pid文件时会失败,说明已经有实例在运行了,那么该进程自动退出,从而保证只有一个实例运行。
代码如下:
#include
#include
#include
#include
#include
#define LOCKMODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
static int tryLockPidFile(int fd)
{
struct flock fl;
fl.l_type = F_WRLCK; /* write lock */
fl.l_start = 0;
fl.l_whence = SEEK_SET;
fl.l_len = 0; //lock the whole file
fl.l_pid = getpid();
return(fcntl(fd, F_SETLK, &fl));
}
static int isInRunning(const char *filename)
{
int fd;
char buf[16];
fd = open(filename, O_RDWR | O_CREAT, LOCKMODE);
if (fd < 0)
{
printf("can't crate/open %s\n", filename);
exit(1);
}
/* try to lock the file */
if (tryLockPidFile (fd) == -1)
{
if (errno == EACCES || errno == EAGAIN)
{
close(fd);
return 1;
}
printf("can't lock %s\n", filename);
exit(1);
}
/* write pid */
ftruncate(fd, 0);
sprintf(buf, "%ld", (long)getpid());
write(fd, buf, strlen(buf) + 1);
// do not close the fd to keep the file lock last all the time of program life
// if close the file, the file-lock close automatic
// close(fd);
return 0;
}