安全地初始化共享内存

发布于 2024-11-30 12:46:14 字数 2292 浏览 1 评论 0原文

我有几个进程通过 OS X 上的 POSIX 共享内存相互通信。 我的问题是这些进程可以以任何顺序生成,并尝试同时初始化共享内存段。

我尝试将咨询锁与 fcntlflock 一起使用,但都失败告诉我我正在传递无效的文件描述符(我确信文件描述符不是无效的)。很明显,这是不可能的。

还有其他选择吗?或者是否有关于使用共享内存锁的任何我不知道的细节?

编辑: 我使用锁的尝试如下所示:

// Some declarations...

struct Queue {                                                                                          
    int index[ENTRIES_PER_QUEUE];                                                                       
    sem_t lock;                                                                                         
    sem_t readWait;                                                                                     
    sem_t writeSem;                                                                                     
    struct Entry slots[ENTRIES_PER_QUEUE];                                                              
};

struct ipc_t {
    int fd;
    char name[512];
    struct Queue* queue;
};

ipc_t ipc_create(const char* name, int owner) {

    int isInited = 1;                                                                                   
    struct Queue* queue;                                                                                
    struct flock lock = {                                                                               
        .l_type = F_WRLCK,                                                                              
        .l_whence = SEEK_SET,                                                                           
        .l_start = 0,                                                                                   
        .l_len = 0                                                                                      
    };

    ipc_t conn = malloc(sizeof(struct ipc_t));

    sprintf(conn->name, "/arqvenger_%s", name);

    conn->fd = shm_open(conn->name, O_CREAT | O_RDWR, 0666);
    if (conn->fd == -1) {
        free(conn);
        perror("shm_open failed");
        return NULL;
    }

    if (fcntl(conn->fd, F_SETLKW, &lock) == -1) {
        perror("Tanked...");
    }

// Do stuff with the lock & release it

我得到的输出是:

Tanked...: Bad file descriptor

I have several processes communicating with each through POSIX shared memory on OS X.
My issue is these processes could spawn in any order, and try to initialize the shared memory segment at the same time.

I tried using advisory locks with fcntl and flock but both fail telling me I'm passing an invalid file descriptor (I'm positive the file descriptor is not invalid). So clearly that's out of the picture.

Are there any alternatives to this? Or is there any details about using locks with shared memory that I'm not aware of?

Edit:
My attempt at using locks looks like this:

// Some declarations...

struct Queue {                                                                                          
    int index[ENTRIES_PER_QUEUE];                                                                       
    sem_t lock;                                                                                         
    sem_t readWait;                                                                                     
    sem_t writeSem;                                                                                     
    struct Entry slots[ENTRIES_PER_QUEUE];                                                              
};

struct ipc_t {
    int fd;
    char name[512];
    struct Queue* queue;
};

ipc_t ipc_create(const char* name, int owner) {

    int isInited = 1;                                                                                   
    struct Queue* queue;                                                                                
    struct flock lock = {                                                                               
        .l_type = F_WRLCK,                                                                              
        .l_whence = SEEK_SET,                                                                           
        .l_start = 0,                                                                                   
        .l_len = 0                                                                                      
    };

    ipc_t conn = malloc(sizeof(struct ipc_t));

    sprintf(conn->name, "/arqvenger_%s", name);

    conn->fd = shm_open(conn->name, O_CREAT | O_RDWR, 0666);
    if (conn->fd == -1) {
        free(conn);
        perror("shm_open failed");
        return NULL;
    }

    if (fcntl(conn->fd, F_SETLKW, &lock) == -1) {
        perror("Tanked...");
    }

// Do stuff with the lock & release it

The output I get is:

Tanked...: Bad file descriptor

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

悲念泪 2024-12-07 12:46:14

一种常见的技术是首先使用 O_CREAT|O_EXCL 调用 shm_open。只有一个必须执行设置的进程才会成功。然后其他人必须像以前一样打开并等待一段时间(可能是轮询)以确保设置完成。

编辑:展示如何按照评论中的讨论进行操作。

struct head {
 unsigned volatile flag;
 pthread_mutex_t mut;
};

void * addr = 0;
/* try shm_open with exclusive, and then */
if (/* we create the segment */) {
  addr = mmap(something);
  struct head* h = addr;
  pthread_mutex_init(&h->mut, aSharedAttr);
  pthread_mutex_lock(&h->mut);
  h->flag = 1;
  /* do the rest of the initialization, and then */
  pthread_mutex_unlock(&h->mut);
} else {
  /* retry shm_open without exclusive, and then */
  addr = mmap(something);
  struct head* h = addr;
  /* initialy flag is guaranteed to be 0 */
  /* this will break out of the loop whence the new value is written to flag */
  while (!h->flag) sched_yield();
  pthread_mutex_lock(&h->mut);
  pthread_mutex_unlock(&h->mut);  
}

A common technique is to first call shm_open with O_CREAT|O_EXCL. This will succeed for only one process that then has to do the setup. The others then would have to do the open as before and wait a bit, probably polling, that the setup is finished.

Edit: To show how this could work as discussed in the comments.

struct head {
 unsigned volatile flag;
 pthread_mutex_t mut;
};

void * addr = 0;
/* try shm_open with exclusive, and then */
if (/* we create the segment */) {
  addr = mmap(something);
  struct head* h = addr;
  pthread_mutex_init(&h->mut, aSharedAttr);
  pthread_mutex_lock(&h->mut);
  h->flag = 1;
  /* do the rest of the initialization, and then */
  pthread_mutex_unlock(&h->mut);
} else {
  /* retry shm_open without exclusive, and then */
  addr = mmap(something);
  struct head* h = addr;
  /* initialy flag is guaranteed to be 0 */
  /* this will break out of the loop whence the new value is written to flag */
  while (!h->flag) sched_yield();
  pthread_mutex_lock(&h->mut);
  pthread_mutex_unlock(&h->mut);  
}
小嗷兮 2024-12-07 12:46:14

我已经能够重现该问题。然后我在标准中发现了一个悲伤的通知:

当文件描述符fildes引用共享内存对象时,
fcntl() 的行为应与常规文件相同,除了
参数 cmd 的以下值的效果应为
未指定:F_SETFL、F_GETLK、F_SETLK 和 F_SETLKW

所以很可能它尚未得到支持。对于这样一个简单的设置,我将使用信号量来表示“内存已准备好”。

编辑

看来我需要提到可以使用“O_EXCL”创建信号量(因此没有竞争)。

I have been able to reproduce the problem. Then I found a sad notice in the standard:

When the file descriptor fildes refers to a shared memory object, the
behavior of fcntl() shall be the same as for a regular file except the
effect of the following values for the argument cmd shall be
unspecified: F_SETFL, F_GETLK, F_SETLK, and F_SETLKW.

So it's likely it's not yet supported. For such a simple setup I would use a semaphore for signaling "the memory is ready".

EDIT

It seems I need to mention semaphores can be created using "O_EXCL" (so there are no races).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文