2010-05-06 49 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