zoukankan      html  css  js  c++  java
  • Linux编程之《只运行一个实例》

    概述

    有些时候,我们要求一个程序在系统中只能启动一个实例。比如,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] = {''};
    
        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
    ", filePath.c_str(), strerror(errno));
                close(fd);
                return false;
            }
        }
    
        // 锁定文件后,将该进程的pid写入文件
        char buf[16] = {''};
        sprintf(buf, "%d", getpid());
        ftruncate(fd, 0);
        ret = write(fd, buf, strlen(buf));
        if (ret < 0)
        {
            printf("Write file failed, file: %s, error: %s
    ", filePath.c_str(), strerror(errno));
            close(fd);
            exit(1);
        }
    
        // 函数返回时不需要调用close(fd)
        // 不然文件锁将失效
        // 程序退出后kernel会自动close
        return true;
    }
    
    int main()
    {
        if (!runSingleInstance())
        {
            printf("Process is already running
    ");
            return 1;
        }
    
        printf("Process start...
    ");
        sleep(5);
        printf("Process end...
    ");
    
        return 0;
    }
    
    
    

    该例子的github地址:https://github.com/chxuan/samples/blob/master/RunSingleInstance/RunSingleInstance.cpp

  • 相关阅读:
    Java中常见的异常
    WebView中Js与Android本地函数的相互调用
    Gson解析POJO类中的泛型参数
    JAVA反射技术的使用
    Couchbase 找回登录密码
    微信内网页支付开发手记
    Android实现自定义字体
    Android实现图片裁切
    Android实现ExpandableTextView可扩展TextView
    仿美团实现地域选择(二)
  • 原文地址:https://www.cnblogs.com/highway-9/p/5517990.html
Copyright © 2011-2022 走看看