简体   繁体   English

shm_open:Mac和Linux之间的差异

[英]shm_open: Differences between Mac and Linux

I have a queue in shared memory. 我在共享内存中有一个队列。 It does work on Linux (kernel 4.3.4), but not on Mac OS X. Are there any differences between how Mac OS X handles shared memory and how linux does, which may explain this? 它适用于Linux(内核4.3.4),但不适用于Mac OS X.Mac OS X如何处理共享内存与linux的工作方式有何不同,这可以解释这一点?

I get the shared memory via: 我通过以下方式获取共享内存:

int sh_fd = shm_open(shmName, O_RDWR | O_CREAT, 
        S_IROTH | S_IWOTH // others hav read/write permission
        | S_IRUSR | S_IWUSR // I have read/write permission
        );

// bring the shared memory to the desired size
ftruncate(sh_fd, getpagesize());

The queue is very simple as well. 队列也很简单。 Here is the basic struct: 这是基本结构:

typedef struct {
// this is to check whether the queue is initialized.
// on linux, this will be 0 initially
bool isInitialized;
// mutex to protect concurrent access
pthread_mutex_t access;
// condition for the reader, readers should wait here
pthread_cond_t reader;
// condition for the writer, writers should wait here
pthread_cond_t writer;
// whether the queue can still be used.
bool isOpen;
// maximum capacity of the queue.
int32_t capacity;
// current position of the reader and number of items.
int32_t readPos, items;

// entries in the queue. The array actually is longer, which means it uses the space behind the struct.
entry entries[1];
} shared_queue;

Basically everyone who wants access acquires the mutex, readPos indicates where the next value should be read (incrementing readPos afterwards), (readPos+items) % capacity is where new items go. 基本上每个想要访问的人都获取互斥锁,readPos指示应该读取下一个值的位置(之后递增readPos),(readPos + items)%capacity是新项目所在的位置。 The only somewhat fancy trick is the isInitialized byte. 唯一有点花哨的技巧是isInitialized字节。 ftruncate fills the shared memory with zeros if it had length 0 before, so I rely on isInitiualized to be zero on a fresh shared memory page and write a 1 there as soon as I initialize the struct. 如果ftruncate之前的长度为0,则ftruncate用零填充共享内存,所以我依赖isInitiualized在新的共享内存页面上为零,并在初始化结构时立即写入1。

As I said, it works on Linux, so I don't think it is a simple implementation bug. 正如我所说,它适用于Linux,因此我认为这不是一个简单的实现错误。 Is there any subtle difference between shm_open on Mac vs. Linux which I may not be aware of? Mac和Linux上的shm_open之间有什么微妙的区别我可能不知道吗? The bug I see looks like the reader tries to read from an empty queue, so, maybe the pthread mutex/condition does not work on shared memory in a Mac? 我看到的错误看起来像读者试图从空队列中读取,所以,也许pthread互斥/条件不适用于Mac上的共享内存?

The problem is that PTHREAD_PROCESS_SHARED is not supported on mac. 问题是mac上不支持PTHREAD_PROCESS_SHARED。

http://alesteska.blogspot.de/2012/08/pthreadprocessshared-not-supported-on.html http://alesteska.blogspot.de/2012/08/pthreadprocessshared-not-supported-on.html

You must set PTHREAD_PROCESS_SHARED on both the mutex and condition variables. 您必须在互斥锁和条件变量上设置PTHREAD_PROCESS_SHARED

So for a mutex: 所以对于互斥量:

pthread_mutexattr_t mutex_attr;
pthread_mutex_t     the_mutex;

pthread_mutexattr_init(&mutex_attr);
pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED);
pthread_mutexattr(&the_mutex, &mutex_attr);

Basically the same steps for the condition variables, but replace mutexattr with condattr . 对于条件变量基本上是相同的步骤,但用mutexattr替换condattr

If the the pthread_*attr_setpshared functions don't exist or return an error, then it may not be supported on your platform. 如果pthread_*attr_setpshared函数不存在或返回错误,则您的平台可能不支持它。

To be on the safe side, you might want to set PTHREAD_MUTEX_ROBUST if supported. 为安全起见,如果支持,您可能需要设置PTHREAD_MUTEX_ROBUST This will prevent deadlock over the mutex (though not guarantee queue consistency) if a process exits while holding the lock. 如果进程在保持锁定时退出,这将防止互斥锁上的死锁(虽然不保证队列一致性)。

EDIT : As an added caution, having a boolean "is initialized" flag is an insufficient plan on its own. 编辑 :作为一个额外的警告,有一个布尔“初始化”标志是一个不充分的计划本身。 You need more than that to really guarantee only one process can initialize the structure. 您需要的不仅仅是确保只有一个进程可以初始化结构。 At the very least you need to do: 至少你需要这样做:

// O_EXCL means this fails if not the first one here
fd = shm_open(name, otherFlags | O_CREAT | O_EXCL );  
if( fd != -1 )
{
   // initialize here

   // Notify everybody the mutex has been initialized.
}
else
{
    fd = shm_open(name, otherFlags ); // NO O_CREAT

    // magically somehow wait until queue is initialized.
}

Are you sure really need to roll your own queue? 你确定需要自己推出自己的队列吗? Will POSIX message queues (see mq_open man page) do the job? POSIX消息队列(请参阅mq_open手册页)是否可以完成这项工作? If not, what about one of many messaging middleware solutions out there? 如果没有,那么许多消息传递中间件解决方案之一呢?

Update 2016-Feb-10: Possible mkfifo based solution 更新2016年2月10日:可能基于mkfifo的解决方案

One alternative to implementing your own queue in shared memory is to use an OS provided named FIFO using mkfifo . 在共享内存中实现自己的队列的另一种方法是使用mkfifo提供的名为FIFO的操作系统。 A key difference between a FIFO and a named pipe is that you are allowed to have multiple simultaneous readers and writers. FIFO和命名管道之间的关键区别在于,您可以同时拥有多个读取器和写入器。

A "catch" to this, is that the reader sees end-of-file when the last writer exits, so if you want readers to go indefinitely, you may need to open a dummy write handle. 这个问题的一个“问题”是读者在最后一个作者退出时看到文件结尾,所以如果你想让读者无限期地去,你可能需要打开一个虚拟的写句柄。

FIFOs are super easy to use on the command line, like so: FIFO在命令行上非常容易使用,如下所示:

reader.sh reader.sh

mkfifo my_queue
cat my_queue

write.sh write.sh

echo "hello world" > my_queue

Or slightly more effort in C: 或者在C中略微努力:

reader.c reader.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>

int main(int argc, char**argv)
{
  FILE * fifo;
  FILE * wfifo;
  int res;
  char buf[1024];
  char * linePtr;

  /* Try to create the queue.  This may belong on reader or writer side
   * depending on your setup. */
  if( 0 !=  mkfifo("work_queue", S_IRUSR | S_IWUSR ) )
  {
    if( errno != EEXIST )
    {
      perror("mkfifo:");
      return -1;
    }
  }

  /* Get a read handle to the queue */
  fifo = fopen("work_queue", "r"); 

  /* Get a write handle to the queue */
  wfifo = fopen("work_queue", "w"); 
  if( !fifo )
  {
    perror("fopen: " );
    return -1;
  }

  while(1)
  {
    /* pull a single message from the queue at a time */
    linePtr = fgets(buf, sizeof(buf), fifo);
    if( linePtr )
    {
      fprintf(stdout, "new command=%s\n", linePtr);
    }
    else
    {
      break;
    }
  } 

  return 0;
}

writer.c writer.c

#include <stdio.h>
#include <unistd.h>
int main(int argc, char**argv)
{
  FILE * pipe = fopen("work_queue", "w");
  unsigned int job = 0;
  int my_pid = getpid(); 
  while(1)
  {
    /* Write one 'entry' to the queue */
    fprintf(pipe, "job %u from %d\n", ++job, my_pid);
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM