Skip to content

运行单一实例

Windows

CreateMutex可以创建或打开一个已命名或未命名的互斥对象

函数原型:

HANDLE WINAPI CreateMutex(
     _In_opt_ LPSECURITY_ATTRIBUTES lpMutexAttributes,
     _In_ BOOL bInitialOwner,
     _In_opt_LPCTSTR lpName
)

lpMutexAttributes指向SECURITY_ATTRIBUTES结构的指针,如果此参数为NULL则此句柄不能由子进程继承

bInitialOwner如果此值为true并且调用者创建了互斥锁,则调用线程将获得互斥锁对象的初始化所有权,否则调用线程不会获得互斥锁的所有权

lpName互斥对象的名称,该名称仅限于MAX_PATH(编译器所支持的最长全路径名的长度),名称区分大小写,如果lpName为NULL,则会创建不带名称的互斥对象嘛,如果lpName与现有事件、信号量、等待定时器、作业或文件映射对象的名称匹配,且这些对象共享相同的命名空间,则该函数将失败,并且GetLastError函数返回ERROR_INVALID_HANDLE

返回值:如果函数成功则返回值是新创建的互斥对象的句柄,如果函数失败则返回NULL,如果互斥锁是一个已命名的互斥锁,并且该对象再此函数调用前就存在,则返回对象是现有对象的句柄,GetLastError返回ERROR_ALREADY_EXISTS

原理是通过CreateMutex函数创建一个命名的互斥对象,如果对象创建成功而且通过调用GetLastError函数获取的返回码为ERROR_ALREADY_EXISTS,则表示该命名互斥对象存在即程序重复运行 编码实现:

BOOL IsAlreadyRun()
{
    HANDLE hMutex = NULL;
    bool result = false;
    hMutex = CreateMutex(NULL, FALSE, TEXT("TEST"));
    if (hMutex)
    {
        if (ERROR_ALREADY_EXISTS == ::GetLastError())
        {
            result= TRUE;
        }
    }
    return result;
}

Linux

Linux查到的方法觉得文件锁方法效果不错,但是需要高权限运行,后续如果发现新的方法再做补充,原理是程序开始运行时,往文件里写入进程号,并锁定文件;此时若再运行一个实例,因前一个实例已经锁定文件,此时是去锁定文件是失败的。

static int lockFile(int fd)
{
    struct flock fl;
    fl.l_type = F_WRLCK;
    fl.l_start = 0;
    fl.l_whence = SEEK_SET;
    fl.l_len = 0;
    fl.l_pid = getpid();  //当前进程号,可写可不写,写了后在用F_GETLK时则可以获取到pid

    int ret = fcntl(fd, F_SETLK, &fl);
    printf("file lock ret = %d\n", ret);
    return ret;
}

static int isRunning(const char *procname)
{
    char buf[16] = {0};
    char fileName[128] = {0};
    snprintf(fileName, sizeof(fileName), "%s%s.pid", PID_FILE_PATH, procname);

    //创建文件时如果指定了 O_CREAT,则必须添加权限,否则可能无法读取文件而导致失败
    int g_fd = open(fileName, O_CREAT | O_RDWR, S_IRWXU);
    if(g_fd < 0)
    {
        printf("open file %s failed !\n", fileName);
        return 1;
    }

    if(-1 == lockFile(g_fd))
    {
        // printf("%s is alread running !\n", procname);
        struct flock fl;
        fl.l_type = F_WRLCK;
        fl.l_start = 0;
        fl.l_whence = SEEK_SET;
        fl.l_len = 0;

        /*这里获取一下已经运行的实例的进程号*/
        if(fcntl(g_fd, F_GETLK, &fl) == 0)
        {
            printf("%s has been run, pid = %d\n", procname, fl.l_pid);
        }

        close(g_fd);
        return 1;
    }
    else
    {
        ftruncate(g_fd, 0);
        long pid = getpid();
        snprintf(buf, sizeof(buf), "%ld", pid);
        printf("write pid = %ld to %s!\n", pid, fileName);

        write(g_fd, buf, strlen(buf) + 1);
        return 0;
    }
}

image-20230801224729634

Comments