2010-05-06 2 views
5

Ermöglicht der POSIX-Standard, dass ein gemeinsamer Speicherblock mit dem Namen eine Mutex- und Bedingungsvariable enthält?Bedingung Variable in Shared Memory - ist dieser Code POSIX-konform?

Wir haben versucht, eine Mutex- und eine Bedingungsvariable zu verwenden, um den Zugriff auf den benannten gemeinsamen Speicher durch zwei Prozesse auf einer LynuxWorks LynxOS-SE system (POSIX-konform) zu synchronisieren.

Ein gemeinsamer Speicherblock heißt "/sync" und enthält die Mutex- und Bedingungsvariable, der andere ist "/data" und enthält die tatsächlichen Daten, auf die wir den Zugriff synchronisieren.

Wir sehen Ausfälle von pthread_cond_signal() wenn beide Prozesse ausführen nicht die mmap() Anrufe in genau die gleiche Reihenfolge, oder wenn ein Prozess mmaps in einige andere Stück gemeinsam genutzten Speicher, bevor es den "/sync" Speicher mmaps.

Dieses Beispiel-Code ist etwa so kurz wie ich es machen kann:

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

Dieses Programm ausführen, ohne args, dann eine andere Kopie mit args, und der erste wird am assert scheitern die pthread_cond_signal() überprüfen. Aber die Reihenfolge der zu mmap() werden open() Funktion ändern Sie die "/sync "Speicher vor den "/data" und alles funktioniert.

Das ist wie ein großer Fehler in LynxOS mir scheint, aber LynuxWorks behauptet, dass mit Mutex und Bedingungsvariablen innerhalb namens Shared Memory auf diese Weise wird durch den POSIX-Standard nicht abgedeckt, so dass sie nicht interessiert.

Kann jemand festzustellen, ob dieser Code nicht verletzt tatsächlich POSIX?
Oder hat jemand eine überzeugende Dokumentation, dass es POSIX-kompatibel ist?

Bearbeiten: wir wissen, dass PTHREAD_PROCESS_SHARED ist POSIX und wird von LynxOS unterstützt. Der Bereich der Konkurrenz ist, ob Mutexe und Semaphore innerhalb des benannten shared memory verwendet werden können (wie wir es getan haben) oder ob POSIX nur erlaubt, sie zu verwenden, wenn ein Prozess den gemeinsamen Speicher erstellt und mmapt und dann den zweiten Prozess gibt.

+1

Ich frage mich, wie sie sich genau vorstellen, die gleiche Variable mit PTHREAD_PROCESS_SHARED zwischen zwei Prozessen zu teilen (was eindeutig besagt, dass * ein * Mechanismus der Aufteilung der Variablen zwischen Prozessen existieren soll.) Und AFAIK verbietet keine Mutexe und Semaphore wo immer du willst , also "nicht abgedeckt" bedeutet "sollte sich wie gewohnt verhalten". –

Antwort

2

Ich kann leicht sehen, wie PTHREAD_PROCESS_SHARED schwierig sein kann auf der OS-Ebene zu implementieren (zB MacOS nicht, außer für Rwlocks scheint es). Aber gerade nachdem Sie den Standard gelesen haben, scheinen Sie einen Fall zu haben.

Für Vollständigkeit, möchten Sie vielleicht auf sysconf(_SC_THREAD_PROCESS_SHARED) und der Rückgabewert des * _setpshared() Funktion aufruft — vielleicht behaupten, es gibt eine andere „Überraschung“ für Sie bereit (aber ich kann aus den Kommentaren sehen, die Sie bereits, dass SHARED geprüft wird tatsächlich unterstützt).

@JesperE: Vielleicht möchten Sie auf die API docs at the OpenGroup statt der HP Dokumente verweisen.

+1

Dank @vs: Ja, ich habe die verschiedenen anderen Behauptungen und Fehlerbehandlung abgeschnitten, um den Code aus Gründen der Kürze zu verhindern, aber seien Sie versichert, dass alle verschiedenen Aufrufe Erfolg zurückgeben, bis die 'pthread_cond_signal()' angezeigt. 'pthread _ * _ setpshared()' wird definitiv unterstützt und ist explizit in den LynxOS Trainingsmaterialien erwähnt (aber sie bieten nur Beispiele, wo ein Prozess gemeinsam genutzten Speicher erstellt, dann fork() s, anstatt zwei Prozesse benannten gemeinsamen Speicher zu verwenden). – GrahamS

+0

Es wurde noch keine echte Antwort gegeben, deshalb werde ich Ihnen die Prämie zusprechen, da Ihre die einzige ist, die sich damit beschäftigt hat, ob dies POSIX-konform ist oder nicht. – GrahamS

4

Die Funktion pthread_mutexattr_setpshared kann verwendet werden, damit ein Pthread-Mutex im Shared Memory von jedem Thread angesprochen werden kann, der Zugriff auf diesen Speicher hat, auch Threads in verschiedenen Prozessen. Gemäß this link entspricht pthread_mutex_setpshared POSIX P1003.1c. (Das Gleiche gilt für die Zustandsgröße, siehe pthread_condattr_setpshared.)

Verwandte Frage: pthread condition variables on Linux, odd behaviour

+0

Danke @JesperE, ich verstehe, dass 'pthread_mutex_setpshared' und' PTHREAD_PROCESS_SHARED' sind POSIX. Ich glaube nicht, dass LynuxWorks das leugnet. Ich denke, der Streit betrifft mehr die Art und Weise, wie wir den gemeinsamen Speicher erzeugen, den der Mutex und Condvar z. Jeder Prozess greift auf den genannten gemeinsamen Speicher zu, anstatt ihn nur in einem Prozess zu erstellen und dann den anderen zu erstellen. – GrahamS

+0

Sorry, ich habe die Frage ein wenig schlampig gelesen. Die man-Seite besagt, dass "diese Option es einem Mutex ermöglicht, von jedem Thread bearbeitet zu werden, der Zugriff auf den Speicher hat, dem der Mutex zugewiesen ist." Es klingt für mich, dass die Art und Weise, wie der Speicher geteilt wird, dem Benutzer überlassen wird, und alle anderen Aufrufe von mmap() sollten die Semantik der Mutex-/Bedingungsvariablen nicht beeinflussen. Warum sollte die gemeinsame Nutzung des Datenbereichs den Mutex überhaupt beeinflussen? Was sagt LynuxWorks über den Standard? Beziehen sie sich auf irgendeinen Platz im Standard oder sind sie nur Handwaving? – JesperE

+0

Ja, LynuxWorks scheint nur Hand zu sein und zu sagen, dass es, wenn es nicht explizit in POSIX angegeben wird, es nicht unterstützt. Ich stimme Ihnen zu: POSIX erlaubt ausdrücklich, dass Mutex und Condvars im Shared Memory erscheinen (was LynxOS unterstützt) - aber ich sehe in POSIX nichts, was die Art und Weise, wie dieser Shared Memory von den Prozessen, die sie teilen, erreicht. – GrahamS

1

Möglicherweise gibt es einige Zeiger in pthread_cond_t (ohne pshared), also müssen Sie es in die gleichen Adressen in beiden threads/processes setzen. Mit gleichgeordneten mmaps erhalten Sie möglicherweise für beide Prozesse die gleichen Adressen.

In glibc war der Zeiger in cond_t Thread-Beschreibung des Threads, Mutex/cond Besitz.

Sie können Adressen mit nicht-NULL ersten Parameter zu mmap steuern.

+0

Yeah ein Zeiger auf den zugrundeliegenden Mutex war unsere Schlussfolgerung auch, aber da wir angegeben haben, dass die Bedingung Variable zwischen den Prozessen geteilt wird, scheint es ein bisschen ein Implementierungsfehler, wenn Sie auch eine mmap Adresse angeben müssen (das ist nur sollte sowieso ein Hinweis sein!) Die einzige zuverlässige Möglichkeit, mmap-Adresshinweise zu verwenden, besteht darin, die im ersten Prozess von mmap() zurückgegebene Adresse als Hinweis für den anderen Prozess zu verwenden, was natürlich Interprozesskommunikation erfordert! :) – GrahamS

+0

@GrahamS, Prozess freigegebene Flag auf Cond_t wird nicht die tatsächlichen Interna der Struktur ändern. Und wenn wir bei der Implementierung einige Zeiger darin haben, die verwendet werden (hmm ..., z.B. Zeiger auf cond_t selbst?), Wird selbst in einer Umgebung mit gemeinsamen Prozessen die Anforderung der gleichen Adressen benötigt. Die Zuverlässigkeit der festen mmap-Adressen hängt von der Plattform ab. Wenn Sie eine Plattform kennen oder einige Konfigurationen haben (dateibasierter Pseudo-IPC), können Sie eine mmap-Adresse auswählen. – osgx