概述

有些时候,我们要求一个程序在系统中只能启动一个实例。比如,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

更多相关文章

  1. Linux系统下Tar文件安装方法
  2. Linux学习笔记(九)--RedHat 7.0之用户身份与文件权限、存储结构与
  3. 获取Android应用程序的Linux UID
  4. 在Linux用tar归档压缩文件时忽略某些文件和目录
  5. 如何查找特定用户可执行的所有文件(不是最新的)
  6. 设置查看linux 造成程序Core dumped 的函数调用层次
  7. iptables:传统的Linux防火墙管理程序
  8. linux socket 程序被ctrl+c或者异常终止,再次起程序时提示该端口
  9. 使用/proc/meminfo文件查看内存状态信息

随机推荐

  1. Android Studio入门之常见问题
  2. Android代码混淆官方实现方法
  3. An_ListView 多样展示
  4. 腾讯开源的Android UI框架——QMUI Andro
  5. android应用开发实战
  6. Android多媒体开发(3)————使用Android
  7. android init.rc 分析
  8. android获取短信所有内容
  9. android 8.0 MountService -->StorageMan
  10. Android 8天入门