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 在线网络考试系统源码 springboot mybaits vue.js 前后分离跨域
    springboot 整合flowable 项目源码 mybiats vue.js 前后分离 跨域
    flowable Springboot vue.js 前后分离 跨域 有代码生成器 工作流
    Flowable 工作流 Springboot vue.js 前后分离 跨域 有代码生成器
    java 企业 网站源码 后台 springmvc SSM 前台 静态化 代码生成器
    java 进销存 商户管理 系统 管理 库存管理 销售报表springmvc SSM项目
    基于FPGA的电子计算器设计(中)
    基于FPGA的电子计算器设计(上)
    FPGA零基础学习:SPI 协议驱动设计
    Signal tap 逻辑分析仪使用教程
  • 原文地址:https://www.cnblogs.com/highway-9/p/5517990.html
Copyright © 2011-2022 走看看