如果在使用PTHREAD_PROCESS_SHARED时不调用pthread_mutex_destroy,会发生什么情况

时间:2020-04-20 12:15:56

标签: c++ c linux pthreads mutex

在Linux上,可以使用PTHREAD_PROCESS_SHARED属性在进程之间共享互斥锁,然后将其保存在许多进程可以使用的映射文件中。

这是https://linux.die.net/man/3/pthread_mutexattr_init中完成上述工作的示例:

For example, the following code implements a simple counting semaphore in a mapped file that may be used by many processes.

/* sem.h */
struct semaphore {
    pthread_mutex_t lock;
    pthread_cond_t nonzero;
    unsigned count;
};
typedef struct semaphore semaphore_t;

semaphore_t *semaphore_create(char *semaphore_name);
semaphore_t *semaphore_open(char *semaphore_name);
void semaphore_post(semaphore_t *semap);
void semaphore_wait(semaphore_t *semap);
void semaphore_close(semaphore_t *semap);

/* sem.c */
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <pthread.h>
#include "sem.h"

semaphore_t *
semaphore_create(char *semaphore_name)
{
int fd;
    semaphore_t *semap;
    pthread_mutexattr_t psharedm;
    pthread_condattr_t psharedc;

    fd = open(semaphore_name, O_RDWR | O_CREAT | O_EXCL, 0666);
    if (fd < 0)
        return (NULL);
    (void) ftruncate(fd, sizeof(semaphore_t));
    (void) pthread_mutexattr_init(&psharedm);
    (void) pthread_mutexattr_setpshared(&psharedm,
        PTHREAD_PROCESS_SHARED);
    (void) pthread_condattr_init(&psharedc);
    (void) pthread_condattr_setpshared(&psharedc,
        PTHREAD_PROCESS_SHARED);
    semap = (semaphore_t *) mmap(NULL, sizeof(semaphore_t),
            PROT_READ | PROT_WRITE, MAP_SHARED,
            fd, 0);
    close (fd);
    (void) pthread_mutex_init(&semap->lock, &psharedm);
    (void) pthread_cond_init(&semap->nonzero, &psharedc);
    semap->count = 0;
    return (semap);
}

semaphore_t *
semaphore_open(char *semaphore_name)
{
    int fd;
    semaphore_t *semap;

    fd = open(semaphore_name, O_RDWR, 0666);
    if (fd < 0)
        return (NULL);
    semap = (semaphore_t *) mmap(NULL, sizeof(semaphore_t),
            PROT_READ | PROT_WRITE, MAP_SHARED,
            fd, 0);
    close (fd);
    return (semap);
}

void
semaphore_post(semaphore_t *semap)
{
    pthread_mutex_lock(&semap->lock);
    if (semap->count == 0)
        pthread_cond_signal(&semapx->nonzero);
    semap->count++;
    pthread_mutex_unlock(&semap->lock);
}

void
semaphore_wait(semaphore_t *semap)
{
    pthread_mutex_lock(&semap->lock);
    while (semap->count == 0)
        pthread_cond_wait(&semap->nonzero, &semap->lock);
    semap->count--;
    pthread_mutex_unlock(&semap->lock);
}

void
semaphore_close(semaphore_t *semap)
{
    munmap((void *) semap, sizeof(semaphore_t));
}
The following code is for three separate processes that create, post, and wait on a semaphore in the file /tmp/semaphore. Once the file is created, the post and wait programs increment and decrement the counting semaphore (waiting and waking as required) even though they did not initialize the semaphore.

/* create.c */
#include "pthread.h"
#include "sem.h"

int
main()
{
    semaphore_t *semap;

    semap = semaphore_create("/tmp/semaphore");
    if (semap == NULL)
        exit(1);
    semaphore_close(semap);
    return (0);
}

/* post */
#include "pthread.h"
#include "sem.h"

int
main()
{
    semaphore_t *semap;

    semap = semaphore_open("/tmp/semaphore");
    if (semap == NULL)
        exit(1);
    semaphore_post(semap);
    semaphore_close(semap);
    return (0);
}

/* wait */
#include "pthread.h"
#include "sem.h"

int
main()
{
    semaphore_t *semap;

    semap = semaphore_open("/tmp/semaphore");
    if (semap == NULL)
        exit(1);
    semaphore_wait(semap);
    semaphore_close(semap);
    return (0);
}

但是在共享互斥锁上调用pthread_mutex_destroy()很棘手,因为它可能导致其他进程出错,并且上面的示例也不调用pthread_mutex_destroy()。所以我想不要破坏它。

我的问题是:如果我初始化一个PTHREAD_PROCESS_SHARED互斥锁,将其保存到映射文件并在许多进程中永久使用它而不调用pthread_mutex_destroy()或重新初始化它,是否安全?

1 个答案:

答案 0 :(得分:1)

我的问题是:如果我初始化一个PTHREAD_PROCESS_SHARED互斥锁,将其保存到映射文件并在许多进程中永久使用它而不调用pthread_mutex_destroy()或重新初始化它,是否安全?

允许进程共享的互斥量超过初始化它的进程的寿命。如果将此类互斥锁映射到持久性常规文件,则即使未映射任何进程,其状态也将无限期持久。只要维持其状态的完整性-包括但不限于没有通过pthread_mutex_destroy()破坏它的进程-新进程就可以映射和使用它。也就是说,您所描述的语义是明确定义的。

但是安全吗?并非如此。

第一个问题是,您需要知道何时创建它,并且在执行操作时需要避免出现竞争情况。如果您依赖定期使用互斥锁进行初始化的进程,则必须确保在文件不存在时恰好创建并初始化该互斥锁。

另一个问题是,使用像这样的长期共享互斥锁会导致大量失败。例如,如果在锁定互斥锁的同时程序崩溃,那么它将保持锁定状态,直到您采取某种手动纠正措施为止。或者,如果直接操作映射文件,则互斥锁状态很容易被破坏,从而在使用该文件的所有程序中产生不确定的行为,即使在重新启动后也是如此。

如果您确实需要一个长期存在的同步对象,那么我建议考虑使用a POSIX named semaphore。它是出于上述目的而设计的,同时考虑了以上因素。但是,有些不同之处在于,此类信号量驻留在内核中并具有内核持久性,因此它们在重新启动后不会持久化(通常是),并且它们不易受普通文件的影响操作。

或者,您可以考虑使用a System V semaphore。这是一个较旧的信号量实现,也具有内核持久性。它的界面比POSIX信号灯笨拙,但是它具有POSIX信号灯不具备的一些有用功能,例如在持有一个锁的进程终止(甚至异常终止)时提供自动解锁。