8

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

不带 args 运行这个程序,然后运行另一个带 args 的副本,第一个将在断言检查pthread_cond_signal(). 但是将open()函数的顺序更改mmap()"/sync" 之前的内存"/data",它就可以正常工作了。

对我来说,这似乎是 LynxOS 中的一个主要错误,但 LynuxWorks 声称以这种方式在命名共享内存中使用互斥锁和条件变量不在 POSIX 标准中,因此他们不感兴趣。

谁能确定这段代码是否确实违反了 POSIX?
或者是否有人有任何令人信服的文档证明它符合 POSIX?

编辑:我们知道这PTHREAD_PROCESS_SHARED是 POSIX 并且由 LynxOS 支持。争论的焦点是互斥体和信号量是否可以在命名共享内存中使用(正如我们所做的那样),或者 POSIX 是否只允许在一个进程创建并映射共享内存然后分叉第二个进程时使用它们。

4

3 回答 3

5

pthread_mutexattr_setpshared函数可用于允许任何有权访问该内存的线程(甚至是不同进程中的线程)访问共享内存中的 pthread 互斥锁。根据此链接pthread_mutex_setpshared符合 POSIX P1003.1c。(条件变量也是如此,请参阅pthread_condattr_setpshared。)

相关问题:Linux 上的 pthread 条件变量,奇怪的行为

于 2010-05-06T17:35:46.933 回答
4

我可以很容易地看到 PTHREAD_PROCESS_SHARED 在操作系统级别上实现起来是多么棘手(例如,MacOS 没有,除了似乎是 rwlocks)。但是仅仅从阅读标准来看,您似乎有一个案例。

为了完整起见,您可能希望在sysconf(_SC_THREAD_PROCESS_SHARED)和 *_setpshared() 函数调用的返回值上声明——也许还有另一个“惊喜”在等着你(但我可以从评论中看到你已经检查过 SHARED 是实际支持)。

@JesperE:您可能想参考OpenGroup 的 API 文档而不是 HP 文档。

于 2010-05-11T08:30:02.607 回答
1

可能有一些指针pthread_cond_t(没有 pshared),所以你必须将它放在两个线程/进程中的相同地址中。使用相同顺序的 mmap,您可能会为两个进程获得相同的地址。

在 glibc 中,cond_t 中的指针指向线程的线程描述符,拥有 mutex/cond。

您可以使用非 NULL 第一个参数来控制 mmap 的地址。

于 2010-05-16T06:23:11.983 回答