shm_开放:Mac和Linux的区别

shm_开放:Mac和Linux的区别,c,linux,macos,shared-memory,C,Linux,Macos,Shared Memory,我在共享内存中有一个队列。它确实可以在Linux内核4.3.4上工作,但不能在Mac OS X上工作。Mac OS X处理共享内存的方式和Linux处理共享内存的方式有什么不同吗?这也许可以解释这一点 我通过以下方式获得共享内存: int sh_fd = shm_open(shmName, O_RDWR | O_CREAT, S_IROTH | S_IWOTH // others hav read/write permission | S_IRUSR | S_I

我在共享内存中有一个队列。它确实可以在Linux内核4.3.4上工作,但不能在Mac OS X上工作。Mac OS X处理共享内存的方式和Linux处理共享内存的方式有什么不同吗?这也许可以解释这一点

我通过以下方式获得共享内存:

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());
队列也很简单。以下是基本结构:

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;
基本上,每个想要访问的人都会获得互斥锁,readPos表示下一个值应该读取的位置,然后递增readPos,readPos+items%容量表示新项目的位置。唯一有点花哨的技巧是isInitialized字节。如果之前共享内存的长度为0,则ftruncate会将其填充为0,因此我在一个新的共享内存页上依赖ISInitialized为0,并在初始化结构后立即在那里写入1

正如我所说,它在Linux上工作,所以我不认为它是一个简单的实现错误。在Mac上的shm_open和Linux之间有没有我可能不知道的细微差别?我看到的错误看起来像是读卡器试图从空队列读取,因此,可能pthread mutex/条件在Mac中的共享内存上不起作用?

您必须在mutex和condition变量上设置pthread\u PROCESS\u shared

因此,对于互斥对象:

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);
条件变量的步骤基本相同,但将mutexattr替换为condattr

如果pthread_*attr_setpshared函数不存在或返回错误,则您的平台可能不支持该函数

为了安全起见,您可能希望将PTHREAD_MUTEX_设置为健壮的(如果支持的话)。这将防止互斥锁上的死锁,但如果进程在持有锁时退出,则不能保证队列一致性

编辑:作为一个额外的警告,单独使用布尔值is initialized标志是不够的。要真正保证只有一个进程可以初始化该结构,您需要更多。至少你需要做:

// 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.
}
你真的需要自己排队吗?POSIX消息队列是否会看到mq_open手册页执行此任务?如果不是的话,那么众多消息传递中间件解决方案中的一个呢

2016年2月10日更新:可能的基于mkfifo的解决方案 在共享内存中实现您自己的队列的一种替代方法是使用一个名为FIFO的操作系统,该操作系统使用mkfifo。FIFO和命名管道之间的一个关键区别是允许同时有多个读写器

其中一个问题是,当最后一个写入程序退出时,读取器会看到文件的结尾,所以如果希望读取器无限期地运行,则可能需要打开一个虚拟写入句柄

FIFO在命令行上非常易于使用,如下所示:

reader.sh write.sh 或者在C中稍微多做一些工作:

读卡器.c 作家c
问题是mac不支持PTHREAD_PROCESS_SHARED


我不明白ISInitialized和ftruncate之间的联系。你能详细解释一下吗?@terencehill-我很确定,在一般情况下,无论他用那个标志做什么花哨的把戏,都不足以阻止两个进程尝试初始化同一个队列,也不足以保证任何进程都不会尝试使用未初始化的队列。请看下面我编辑的答案…是的,你是对的,bool标志是不够的。我通常只等几秒钟就开始第二个过程。谢谢你的建议。对于mq_open:如果我搜索mac mq_open,这个线程是第七个命中。如果您确定linux上的Mac OS X上存在此函数,这可能是一个好主意。是POSIX的东西,而不是特定于Linux的。但奇怪的是,我想你是对的,OSX不支持它。OSX确实支持System V消息队列—请参阅msgctl、msgsnd等,这也很奇怪。@Alex-您可以试试,它为您提供了一个多读取器、多写入器安全队列。唯一需要注意的技巧是,当最后一个writer关闭时,所有读者都会看到一个EOF。@Alex-有关使用mkfifo的快速示例,请参阅更新的答案。看起来您是对的,这意味着Alex的解决方案无法移植到OSX。
mkfifo my_queue
cat my_queue
echo "hello world" > my_queue
#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;
}
#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);
  }
}