Переменная условия в общей памяти - соответствует ли этот код POSIX? - PullRequest
5 голосов
/ 06 мая 2010

Позволяет ли стандарт POSIX блок общей памяти с именем содержать мьютекс и переменную условия?

Мы пытались использовать переменную мьютекса и условия для синхронизации доступа к именованной совместно используемой памяти двумя процессами в системе LynuxWorks LynxOS-SE (POSIX-совместимой).

Один блок общей памяти называется "/sync" и содержит мьютекс и переменную условия, другой - "/data" и содержит фактические данные, к которым мы синхронизируем доступ.

Мы видим сбои из pthread_cond_signal(), если оба процесса не выполняют вызовы mmap() в точно такого же порядка , или если один процесс отображает в какой-то другой часть общей памяти, прежде чем она отобразит память "/sync".

Этот пример кода настолько короток, насколько я могу его представить:

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/file.h>
#include <stdlib.h>
#include <pthread.h>
#include <errno.h>
#include <iostream>
#include <string>
using namespace std;

static const string shm_name_sync("/sync");
static const string shm_name_data("/data");

struct shared_memory_sync
{
    pthread_mutex_t mutex;
    pthread_cond_t condition;
};

struct shared_memory_data
{
    int a;
    int b;
};


//Create 2 shared memory objects
// - sync contains 2 shared synchronisation objects (mutex and condition)
// - data not important 
void create()
{
    // Create and map 'sync' shared memory
    int fd_sync = shm_open(shm_name_sync.c_str(), O_CREAT|O_RDWR, S_IRUSR|S_IWUSR);
    ftruncate(fd_sync, sizeof(shared_memory_sync));
    void* addr_sync = mmap(0, sizeof(shared_memory_sync), PROT_READ|PROT_WRITE, MAP_SHARED, fd_sync, 0);
    shared_memory_sync* p_sync = static_cast<shared_memory_sync*> (addr_sync);

    // init the cond and mutex
    pthread_condattr_t cond_attr;
    pthread_condattr_init(&cond_attr);
    pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_SHARED);
    pthread_cond_init(&(p_sync->condition), &cond_attr);
    pthread_condattr_destroy(&cond_attr);

    pthread_mutexattr_t m_attr;
    pthread_mutexattr_init(&m_attr);
    pthread_mutexattr_setpshared(&m_attr, PTHREAD_PROCESS_SHARED);
    pthread_mutex_init(&(p_sync->mutex), &m_attr);
    pthread_mutexattr_destroy(&m_attr);

    // Create the 'data' shared memory   
    int fd_data = shm_open(shm_name_data.c_str(), O_CREAT|O_RDWR, S_IRUSR|S_IWUSR);
    ftruncate(fd_data, sizeof(shared_memory_data));

    void* addr_data = mmap(0, sizeof(shared_memory_data), PROT_READ|PROT_WRITE, MAP_SHARED, fd_data, 0);
    shared_memory_data* p_data = static_cast<shared_memory_data*> (addr_data);

    // Run the second process while it sleeps here.
    sleep(10);

    int res = pthread_cond_signal(&(p_sync->condition));
    assert(res==0);  // <--- !!!THIS ASSERT WILL FAIL ON LYNXOS!!!

    munmap(addr_sync, sizeof(shared_memory_sync));
    shm_unlink(shm_name_sync.c_str());
    munmap(addr_data, sizeof(shared_memory_data));
    shm_unlink(shm_name_data.c_str());
}

//Open the same 2 shared memory objects but in reverse order
// - data
// - sync 
void open()
{
    sleep(2);
    int fd_data = shm_open(shm_name_data.c_str(), O_RDWR, S_IRUSR|S_IWUSR);
    void* addr_data = mmap(0, sizeof(shared_memory_data), PROT_READ|PROT_WRITE, MAP_SHARED, fd_data, 0);
    shared_memory_data* p_data = static_cast<shared_memory_data*> (addr_data);

    int fd_sync = shm_open(shm_name_sync.c_str(), O_RDWR, S_IRUSR|S_IWUSR);
    void* addr_sync = mmap(0, sizeof(shared_memory_sync), PROT_READ|PROT_WRITE, MAP_SHARED, fd_sync, 0);
    shared_memory_sync* p_sync = static_cast<shared_memory_sync*> (addr_sync);

    // Wait on the condvar
    pthread_mutex_lock(&(p_sync->mutex));
    pthread_cond_wait(&(p_sync->condition), &(p_sync->mutex));
    pthread_mutex_unlock(&(p_sync->mutex));

    munmap(addr_sync, sizeof(shared_memory_sync));
    munmap(addr_data, sizeof(shared_memory_data));
}

int main(int argc, char** argv) 
{
    if(argc>1)
    {
        open(); 
    }
    else
    {
        create();
    }

    return (0);
}

Запустите эту программу без аргументов, затем еще одну копию с аргументами, и первая из них потерпит неудачу при проверке утверждения pthread_cond_signal(). Но измените порядок функции open() на mmap() память "/sync "до "/data", и все будет работать нормально.

Это кажется мне серьезной ошибкой в ​​LynxOS, но LynuxWorks утверждает, что использование мьютекса и условной переменной в именованной общей памяти таким образом не охватывается стандартом POSIX, поэтому они не интересуются.

Может кто-нибудь определить, действительно ли этот код нарушает POSIX?
Или у кого-нибудь есть какая-нибудь убедительная документация о том, что она POSIX-совместима?

Редактировать : мы знаем, что PTHREAD_PROCESS_SHARED - это POSIX и поддерживается LynxOS. Спорным вопросом является то, могут ли мьютексы и семафоры использоваться в именованной общей памяти (как мы это сделали) или POSIX позволяет использовать их только тогда, когда один процесс создает и отображает общую память, а затем разветвляет второй процесс.

Ответы [ 3 ]

4 голосов
/ 06 мая 2010

Функция pthread_mutexattr_setpshared может использоваться для обеспечения доступа к мьютексу pthread в разделяемой памяти любым потоком, который имеет доступ к этой памяти, даже потокам в разных процессах. Согласно эта ссылка , pthread_mutex_setpshared соответствует POSIX P1003.1c. (То же самое относится и к условной переменной, см. pthread_condattr_setpshared.)

Смежный вопрос: условные переменные в Linux, странное поведение

2 голосов
/ 11 мая 2010

Я легко вижу, как PTHREAD_PROCESS_SHARED может быть сложно реализовать на уровне ОС (например, MacOS не делает, за исключением rwlocks, кажется).Но только после прочтения стандарта у вас, похоже, есть регистр.

Для полноты вы можете захотеть установить в sysconf (_SC_THREAD_PROCESS_SHARED) и возвращаемое значение функции * _setpshared ()звонки - может быть, вас ждет еще один «сюрприз» (но по комментариям вы уже убедились, что SHARED действительно поддерживается).

@ JesperE: возможно, вы захотите обратиться к APIдокументы в OpenGroup вместо документов HP.

1 голос
/ 16 мая 2010

Может быть, в pthread_cond_t есть некоторые указатели (без pshared), поэтому вы должны поместить его по одинаковым адресам в обоих потоках / процессах. С одинаковыми mmaps вы можете получить одинаковые адреса для обоих процессов.

В glibc указатель в cond_t был на дескриптор потока, принадлежащего mutex / cond.

Вы можете управлять адресами с ненулевым первым параметром для mmap.

...