[英]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? 如果没有,那么许多消息传递中间件解决方案之一呢?
mkfifo
based solution 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在命令行上非常容易使用,如下所示:
mkfifo my_queue
cat my_queue
echo "hello world" > my_queue
Or slightly more effort in C: 或者在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;
}
#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.