2010-05-06 30 views
5

POSIX標準允許名爲共享內存塊包含互斥和條件變量嗎?共享內存中的條件變量 - 此代碼是否符合POSIX標準?

我們一直試圖使用一個互斥鎖和條件變量來同步兩個進程(POSIX-conformant)上的兩個進程對命名共享內存的訪問。

一個共享內存塊被稱爲"/sync"幷包含互斥鎖和條件變量,另一個是"/data"幷包含我們正在同步訪問的實際數據。

我們從pthread_cond_signal()看到失敗,如果這兩個過程不執行mmap()電話完全相同的順序,或者如果在一個過程mmaps其他一些一塊共享內存的它mmaps的"/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); 
} 

運行此程序與無參數,然後另一個副本ARGS,並第一個將在斷言檢查pthread_cond_signal()失敗。 但open()功能的順序更改爲mmap()"/data""/sync「的內存和它都做工精細。

這似乎是在LynxOS的給我一個大錯誤,但LynuxWorks公司聲稱,使用內互斥和條件變量以這種方式命名的共享內存不被POSIX標準所覆蓋,所以他們不感興趣。

任何人都可以判斷這段代碼實際上確實違反POSIX?
或任何人有任何令人信服的記錄,這是POSIX兼容?

編輯:我們知道PTHREAD_PROCESS_SHARED是POSIX,並且由LynxOS支持。爭用的領域是互斥鎖和信號量是否可以在命名共享內存中使用(就像我們已經完成的一樣),或者POSIX只允許在一個進程創建時使用它們,然後映射共享內存,然後分叉第二個進程。

+1

我不知道他們究竟是如何想象共享兩個進程之間使用PTHREAD_PROCESS_SHARED相同的變量(這顯然意味着*的共享流程之間的可變部分*機制應該存在的。)而據我所知沒有標準禁止將互斥和信號燈無論你是否想,所以「未覆蓋」意味着「應該照常運行」。 – 2017-06-01 08:31:08

回答

2

我可以很容易地看到PTHREAD_PROCESS_SHARED怎麼可能會非常棘手的操作系統級別來實現(例如MacOS的沒有,除了看起來好像是)。但僅僅通過閱讀標準,你似乎有一個案例。

爲了完整起見,你可能想主張對sysconf(_SC_THREAD_PROCESS_SHARED)和* _setpshared的返回值()函數調用—也許還有另外一個「驚喜」等着你(但我可以從你已經檢查了共享評論見實際上是支持的)。

@JesperE:您可能想引用API docs at the OpenGroup而不是HP文檔。

+1

謝謝@vs:是的,我刪除了各種其他斷言和錯誤處理以簡化代碼以防止代碼,但請放心,所有各種調用都會返回成功,直到'pthread_cond_signal()'指示爲止。 ''pthread _ * _ setpshared()'是絕對支持的,並且在LynxOS培訓資料中明確提到過(但它們僅提供一個例子,其中一個進程創建共享內存,然後fork(),而不是使用命名共享內存的兩個進程。 – GrahamS 2010-05-11 10:05:20

+0

尚未提供真正的答案,所以我將獎勵您的賞金,因爲您的是唯一一個處理這是否符合POSIX標準的。 – GrahamS 2010-05-16 08:34:44

4

pthread_mutexattr_setpshared函數可用於允許任何有權訪問該內存的線程訪問共享內存中的pthread互斥鎖,即使是不同進程中的線程也可訪問該線程。根據this linkpthread_mutex_setpshared符合POSIX P1003.1c。 (同樣的事情會在條件變量,見pthread_condattr_setpshared。)

相關問題:pthread condition variables on Linux, odd behaviour

+0

感謝@JesperE,我明白'pthread_mutex_setpshared'和'PTHREAD_PROCESS_SHARED'是POSIX。我不認爲LynuxWorks會否認這一點。我認爲這個爭論更多的是關於我們如何創建共享內存的方式,例如互斥鎖和condvar在例如每個進程通過命名共享內存訪問,而不是隻在一個進程中創建它,然後分叉創建另一個進程。 – GrahamS 2010-05-06 21:24:50

+0

對不起,我讀了一下這個問題。該手冊頁說:「這個選項允許任何有權訪問互斥量被分配的內存的線程操作互斥體。」在我看來,內存如何共享取決於用戶,並且對mmap()的任何其他調用都不應該影響互斥/條件變量語義。爲什麼共享數據區會影響互斥量呢? LynuxWorks聲稱這個標準說什麼?他們是否指向標準中的任何地方,還是僅僅是手動操作? – JesperE 2010-05-07 07:30:41

+0

是的,LynuxWorks似乎只是在交談,並說如果沒有在POSIX中明確指定,那麼他們不支持它。我同意你的看法:POSIX顯式允許互斥鎖和condvars出現在共享內存中(LynxOS支持) - 但我沒有看到POSIX中的任何內容限制共享內存的共享內存。 – GrahamS 2010-05-07 11:09:30

1

可能在pthread_cond_t(沒有pshared)中有一些指針,因此您必須將它放在兩個線程/進程中的相同地址中。使用相同的mmap可以爲這兩個進程獲得相同的地址。

在glibc中,cond_t中的指針是線程的描述符,擁有互斥/ cond。

您可以使用非NULL第一個參數控制mmap的地址。

+0

是的,一個指向底層互斥體的指針也是我們的結論,但是因爲我們已經指出條件變量將在進程間共享,所以如果你還需要指定一個mmap地址(這是唯一的應該是一個提示!)使用mmap地址提示的唯一可靠方法是使用第一個進程中mmap()返回的地址作爲其他進程的提示,這當然需要進程間通信! :) – GrahamS 2010-05-16 08:30:47

+0

@GrahamS,cond_t上的進程共享標誌不會改變結構的實際內部。如果在實現中我們有一些指針,它們將被使用(嗯......例如指向cond_t本身的指針?),即使在共享進程的環境中,也會需要相同的地址。固定mmap地址的可靠性取決於平臺。它你知道平臺或有一些配置(基於文件的僞IPC),你可以選擇一些mmap地址。 – osgx 2010-05-16 22:02:52