概述
有些時候,我們要求一個程序在系統中只能啟動一個實例。比如,Windows自帶的播放軟件Windows Medea Player在Windows里就只能啟動一個實例。原因很簡單,如果同時啟動幾個實例,卻播放不同的文件,那么聲音和圖像就會引起混亂。在設計模式中,就有一個SINGLETON模式。對於程序而言,我們只有在程序啟動的時候去檢測某個設置,如果程序沒有啟動,就把設置更新為程序已經啟動,然后正常啟動程序;如果程序已經啟動,那么就終止程序的啟動。在程序退出的時候把設置恢復為程序沒有啟動。按照上面的思路,我們很容易就能想出一些方法:
- 文件鎖
- 共享內存
- 管道
- 注冊表(windows實現)
- etc...
該實例是使用文件鎖來實現單例的,下面將完整代碼貼上。
/************************************************
* 該例程講解Linux下程序只運行一個實例的編程實現
*
* 編寫只運行一個實例的程序有很多種方式,比如通過管道
* 共享內存、文件鎖等,主要是要有一個全局flag標志該程序
* 已經在運行了,本程序使用文件鎖來實現單實例
************************************************/
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#ifndef PATH_MAX
#define PATH_MAX 1024 // 默認最大路徑長度
#endif
std::string currentExeName()
{
char buf[PATH_MAX] = {'\0'};
int ret = readlink("/proc/self/exe", buf, PATH_MAX);
if (ret < 0 || ret >= PATH_MAX)
{
return "";
}
std::string path(buf);
int pos = path.find_last_of("/");
if (pos == -1)
{
return "";
}
path = path.substr(pos + 1, path.size() - 1);
return path;
}
bool runSingleInstance()
{
// 獲取當前可執行文件名
std::string processName = currentExeName();
if (processName.empty())
{
exit(1);
}
// 打開或創建一個文件
std::string filePath = std::string("/var/run/") + processName + ".pid";
int fd = open(filePath.c_str(), O_RDWR | O_CREAT, 0666);
if (fd < 0)
{
printf("Open file failed, error : %s", strerror(errno));
exit(1);
}
// 將該文件鎖定
// 鎖定后的文件將不能夠再次鎖定
struct flock fl;
fl.l_type = F_WRLCK; // 寫文件鎖定
fl.l_start = 0;
fl.l_whence = SEEK_SET;
fl.l_len = 0;
int ret = fcntl(fd, F_SETLK, &fl);
if (ret < 0)
{
if (errno == EACCES || errno == EAGAIN)
{
printf("%s already locked, error: %s\n", filePath.c_str(), strerror(errno));
close(fd);
return false;
}
}
// 鎖定文件后,將該進程的pid寫入文件
char buf[16] = {'\0'};
sprintf(buf, "%d", getpid());
ftruncate(fd, 0);
ret = write(fd, buf, strlen(buf));
if (ret < 0)
{
printf("Write file failed, file: %s, error: %s\n", filePath.c_str(), strerror(errno));
close(fd);
exit(1);
}
// 函數返回時不需要調用close(fd)
// 不然文件鎖將失效
// 程序退出后kernel會自動close
return true;
}
int main()
{
if (!runSingleInstance())
{
printf("Process is already running\n");
return 1;
}
printf("Process start...\n");
sleep(5);
printf("Process end...\n");
return 0;
}
該例子的github地址:https://github.com/chxuan/samples/blob/master/RunSingleInstance/RunSingleInstance.cpp