Что произойдет, если я не вызову pthread_mutex_destroy при использовании PTHREAD_PROCESS_SHARED - PullRequest
1 голос
/ 20 апреля 2020

В 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 Ответ

1 голос
/ 20 апреля 2020

У меня вопрос: безопасно ли инициировать мьютекс PTHREAD_PROCESS_SHARED, сохранить его в отображенном файле и использовать его навсегда во многих процессах без вызова pthread_mutex_destroy () или повторно инициализировать его?

Разрешено, что разделяемый процессом мьютекс переживает процесс, который его инициализировал. Если вы сопоставите такой мьютекс с постоянным обычным файлом, его состояние будет сохраняться бесконечно, даже если ни один процесс не отобразил его. Пока поддерживается целостность его состояния, включая, но не ограничиваясь этим, ни один процесс, разрушающий его через pthread_mutex_destroy(), новые процессы могут отобразить его и использовать. То есть семантика того, что вы описываете, четко определена.

Но действительно ли это безопасно ? Не особенно.

Первая проблема заключается в том, что вам нужно знать, когда его создавать, и вам следует избегать условий гонки, когда вы это делаете. Если вы полагаетесь на процессы, которые регулярно используют мьютекс, чтобы инициализировать его при необходимости, то вы должны убедиться, что именно он создает и инициализирует его, когда файл еще не существует.

Другая проблема заключается в том, что при использовании долгоживущий общий мьютекс, подобный этому, в значительной степени подвержен сбоям. Например, если происходит сбой программы при удержании мьютекса заблокированным, он останется заблокированным до тех пор, пока вы не предпримете какие-то действия по исправлению вручную. Или, если сопоставленный файл управляется напрямую, состояние мьютекса может быть легко повреждено, что приведет к неопределенному поведению во всех программах, использующих его, даже при перезагрузках.

Если вам действительно нужен долго сохраняющийся объект синхронизации, тогда я предложил бы рассмотреть семафор POSIX с именем . Это разработано для цели, принимая во внимание вышеупомянутые соображения и другие. Однако он несколько отличается тем, что такие семафоры находятся в ядре и имеют постоянство в ядре, поэтому они не сохраняются при перезагрузках (что обычно является хорошим ), и они не подвержены обычным манипуляциям с файлами .

В качестве альтернативы вы можете рассмотреть семафор System V . Это более старая реализация семафора, которая также имеет постоянство ядра. Его интерфейс значительно более громоздкий, чем у семафора POSIX, но у него есть несколько полезных функций, которых нет у семафора POSIX, таких как автоматическая разблокировка c, когда процесс, удерживающий один заблокированный, завершается (даже ненормально).

...