zoukankan      html  css  js  c++  java
  • 实现单实例的应用程序

    今天面试,做了两道面试题,有道题,也是非常的有趣,下面简单的说说,原题是:"有哪些方法可以实现单实例的应用程序?选择一种方法阐述它到底实现原理,流程及优缺点?

      然后他们给的答案是:信号量,共享内存,命名管道,绑定端口等。

      我顿时感觉整个人都不好了,首先想到的是微软操作系统的单实例的应用程序,再转到c++方面,也只有单例模型了。在我的脑海里没有关于linux这些单实例的知识。从网上找了找,看了看,说的也是有道理,是在下知识狭隘了。

    转自http://www.cnblogs.com/highway-9/p/5517990.html

    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;
    }

    转自 http://www.linuxdiyf.com/linux/18341.html
    Linux下程序单例模式的保证机制:/var/run/*.pid
    在Linux 系统中/var/run下有很多以pid结尾的文件,这个其实是为了保证程序以单例模式运行而设计的。程序在启动后,首先打开(如果没有则创建)/var/run/xx.pid,然后尝试去设置文件锁,如果成功,则将程序的里程ID写入该文件,写入后注意不要关闭文件或解锁;如果加锁失败,表明程序已经有一个进程在运行了,则退出此次启动。此机制在一些程序尤其是服务器程序中很常见,例如sip服务器kamailio中就是这样保证程序以单例模式运行的。

    注:程序退出后,文件锁自动解锁。

    #include <stdio.h> 
    #include <string.h> 
    #include <fcntl.h> 
    #include <stdlib.h> 
    #define DEFAULT_FILE "/var/run/test.pid" 

    int main(int argc, char *argv[]) 

    int fd = -1; 
    char buf[32]; 
    fd = open(DEFAULT_FILE, O_WRONLY | O_CREAT, 0666); 
    if (fd < 0) { 
    perror("Fail to open"); 
    exit(1); 

    struct flock lock; 
    bzero(&lock, sizeof(lock)); 
    if (fcntl(fd, F_GETLK, &lock) < 0) { 
    perror("Fail to fcntl F_GETLK"); 
    exit(1); 

    lock.l_type = F_WRLCK; 
    lock.l_whence = SEEK_SET; 
    if (fcntl(fd, F_SETLK, &lock) < 0) { 
    perror("Fail to fcntl F_SETLK"); 
    exit(1); 

    pid_t pid = getpid(); 
    int len = snprintf(buf, 32, "%d ", (int)pid); 
    write(fd, buf, len); //Write pid to the file 

    printf("Hello world "); 

    while(1); 
    return 0; 
    }
    编译:
    gcc -o test test.c

    运行:

    1.打开终端: ./test

    cat /var/run/test.pid
    23409
    2.打开另一终端:./test,打印错误如下:

    Fail to fcntl F_SETLK: Resource temporarily unavailable

    表示程序已经有实例运行。

    from:https://blog.csdn.net/wrzfeijianshen/article/details/53206382

  • 相关阅读:
    jquery总结01-基本概念和选择器
    git学习笔记总结
    git学习笔记12-标签管理-版本
    vue 图片下载到本地,图片保存到本地
    Failed to mount component: template or render function not defined.
    javascript 错误监控
    javascript 跨域 的几种方法
    解决跨域脚本攻击 XSS
    标准时间转YYYY-MMM-DD
    javascript时间戳和日期字符串相互转换
  • 原文地址:https://www.cnblogs.com/lidabo/p/10250809.html
Copyright © 2011-2022 走看看