2010-05-06 14 views
5

Lo standard POSIX consente a un denominato di memoria condivisa di contenere un mutex e una variabile di condizione?Condizione variabile nella memoria condivisa - questo codice è conforme al codice POSIX?

Abbiamo cercato di utilizzare una variabile mutex e condizione per sincronizzare l'accesso alla memoria condivisa denominata da due processi su un LynuxWorks LynxOS-SE system (conforme a POSIX).

Un blocco di memoria condivisa è chiamato "/sync" e contiene il mutex e la variabile di condizione, l'altro è "/data" e contiene i dati effettivi a cui si sta sincronizzando l'accesso.

Stiamo vedendo gli errori da pthread_cond_signal() se entrambi i processi non svolgono le mmap() chiamate in esattamente nello stesso ordine, oppure, se uno mmaps processo in qualche altro pezzo di memoria condivisa prima che la memoria mmaps "/sync".

Questo codice di esempio è di circa il più breve posso farlo:

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

Esegui il programma senza argomenti, poi un'altra copia con args, e il primo che non riuscirà a l'asserzione verifica della pthread_cond_signal(). Ma cambiare l'ordine della funzione open() al mmap() "memoria "/sync prima del "/data" e sarà tutto bene il lavoro.

Questo mi sembra un bug importante in LynxOS a me, ma LynuxWorks sostengono che usando mutex e variabili condizioni all'interno memoria condivisa chiamato in questo modo non è coperto dallo standard POSIX, quindi non sono interessati.

qualcuno può determinare se questo codice fa in realtà viola POSIX?
O qualcuno ha qualche documentazione convincente che è conforme a POSIX?

Modifica: sappiamo che PTHREAD_PROCESS_SHARED è POSIX ed è supportato da LynxOS. L'area di conflitto è se i mutex e i semafori possono essere utilizzati all'interno della memoria condivisa denominata (come abbiamo fatto) o se POSIX li consente di essere utilizzati solo quando un processo crea e mmap la memoria condivisa e quindi avvia il secondo processo.

+1

Mi chiedo come immaginino esattamente di condividere la stessa variabile usando PTHREAD_PROCESS_SHARED tra due processi (il che implica chiaramente * alcuni * meccanismi di condivisione della variabile tra i processi dovrebbe esistere.) E AFAIK nessuno standard proibisce il posizionamento di mutex e semafori ovunque tu voglia , quindi "non coperto" significa "dovrebbe comportarsi come al solito". –

risposta

2

posso facilmente vedere come PTHREAD_PROCESS_SHARED può essere difficile da implementare sul livello di sistema operativo (ad esempio, MacOS non lo fa, ad eccezione di rwlocks sembra). Ma solo leggendo lo standard, sembra che tu abbia un caso.

Per completezza, si potrebbe desiderare di far valere sul sysconf(_SC_THREAD_PROCESS_SHARED) e il valore di ritorno del * _setpshared() chiama — forse c'è un'altra "sorpresa" in attesa per voi (ma posso vedere dai commenti che hai già verificato che CONDIVISO è effettivamente supportato).

@JesperE: è possibile fare riferimento allo API docs at the OpenGroup anziché ai documenti HP.

+1

Grazie a @vs: Sì, ho ritagliato i vari altri asseriti e la gestione degli errori per evitare il codice per brevità, ma mi assicuro che tutte le varie chiamate restituiscano successo finché non viene indicato 'pthread_cond_signal()'. 'pthread _ * _ setpshared()' è decisamente supportato ed è esplicitamente menzionato nei materiali di formazione LynxOS (ma forniscono solo esempi in cui un processo crea memoria condivisa allora fork() s, piuttosto che due processi che utilizzano la memoria condivisa denominata). – GrahamS

+0

Nessuna risposta reale è stata ancora offerta, quindi ho intenzione di assegnarti la taglia in quanto il tuo è l'unico che ha affrontato se questo è compatibile con POSIX o no. – GrahamS

4

La funzione pthread_mutexattr_setpshared può essere utilizzata per consentire l'accesso a un mutex pthread in memoria condivisa da qualsiasi thread che abbia accesso a tale memoria, anche thread in processi diversi. Secondo this link, pthread_mutex_setpshared è conforme a POSIX P1003.1c. (Stessa cosa vale per la variabile di condizione, vedi pthread_condattr_setpshared.)

questione connessa: pthread condition variables on Linux, odd behaviour

+0

Grazie a @JesperE, capisco che 'pthread_mutex_setpshared' e' PTHREAD_PROCESS_SHARED' sono POSIX. Non penso che LynuxWorks lo stia negando. Penso che la disputa riguardi il modo in cui stiamo creando la memoria condivisa che il mutex e il condvar sono ad es. ogni processo accedendo tramite la memoria condivisa denominata, invece di crearlo in un unico processo e quindi biforcarsi per creare l'altro. – GrahamS

+0

Scusa, ho letto la domanda un po 'sciatta. La pagina di manuale dice che "questa opzione consente a un mutex di essere gestito da qualsiasi thread che ha accesso alla memoria in cui è allocato il mutex." Mi sembra che il modo in cui la memoria è condivisa sia all'utente, e qualsiasi altra chiamata a mmap() non dovrebbe influenzare la semantica della variabile mutex/condizione. Perché la condivisione dell'area dati dovrebbe influenzare il mutex? Cosa sostiene LynuxWorks secondo lo standard? Si stanno riferendo a qualsiasi posto nello standard, o stanno semplicemente facendo handwaving? – JesperE

+0

Sì, sembra che LynuxWorks stia facendo il handwaving e dicendo che se non è specificato esplicitamente in POSIX, non lo supportano. Sono d'accordo con te: POSIX consente esplicitamente a mutex e condvars di apparire nella memoria condivisa (supportata da LynxOS), ma non vedo nulla in POSIX che limiti il ​​modo in cui tale memoria condivisa viene acquisita dai processi che la condividono. – GrahamS

1

Potrebbe esserci qualche puntatore in pthread_cond_t (senza pshared), quindi è necessario inserirlo negli stessi indirizzi in entrambi i thread/processi. Con mmaps identici, è possibile ottenere indirizzi uguali per entrambi i processi.

In glibc il puntatore in cond_t era il descrittore di thread del thread, di proprietà mutex/cond.

È possibile controllare gli indirizzi con il primo parametro non NULL in mmap.

+0

Sì, un puntatore al mutex sottostante era anche la nostra conclusione, ma poiché abbiamo indicato che la variabile di condizione sarà condivisa tra i processi, sembra un po 'un difetto di implementazione se devi anche specificare un indirizzo mmap (che è solo dovrebbe essere un suggerimento comunque!) L'unico modo affidabile per usare gli hms degli indirizzi mmap è usare l'indirizzo restituito da mmap() nel primo processo come suggerimento per l'altro processo, che ovviamente richiede la comunicazione tra processi! :) – GrahamS

+0

@GrahamS, il contrassegno di processo condiviso su cond_t non cambierà gli interni effettivi della struttura. E se nell'implementazione abbiamo al suo interno dei puntatori, che saranno usati (hmm ... ad esempio puntatore a cond_t stesso?), Anche in ambiente process-shared ci sarà il requisito degli indirizzi uguali. l'affidabilità degli indirizzi fissi di mmap dipende dalla piattaforma. Conoscendo la piattaforma o alcune configurazioni (pseudo IPC basato su file), è possibile selezionare un indirizzo mmap. – osgx

Problemi correlati