Безопасная инициализация общей памяти - PullRequest
1 голос
/ 21 августа 2011

У меня есть несколько процессов, взаимодействующих с каждым через общую память POSIX в OS X. Моя проблема в том, что эти процессы могут порождаться в любом порядке и пытаться инициализировать сегмент общей памяти одновременно.

Я пытался использовать консультативные блокировки с fcntl и flock, но оба не смогли сказать, что я передаю неверный дескриптор файла (я уверен, что дескриптор файла не является недействительным). Так ясно, что это за кадром.

Есть ли альтернативы этому? Или есть какие-то подробности об использовании замков с разделяемой памятью, о которых я не знаю?

Edit: Моя попытка использования замков выглядит так:

// 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

Ответы [ 2 ]

4 голосов
/ 21 августа 2011

Обычная техника - сначала позвонить shm_open с O_CREAT|O_EXCL. Это будет успешным только для одного процесса, который затем должен выполнить настройку. Остальные затем должны будут сделать открытие, как и раньше, и немного подождать, возможно, опросив, что установка завершена.

Редактировать: Чтобы показать, как это может работать, как описано в комментариях.

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);  
}
3 голосов
/ 21 августа 2011

Мне удалось воспроизвести проблему.Затем я обнаружил печальное замечание в стандартном :

Когда файловый дескриптор fildes ссылается на объект общей памяти, поведение fcntl () должно быть таким же, как и дляобычный файл, за исключением действия следующих значений для аргумента cmd, не должен указываться: F_SETFL, F_GETLK, F_SETLK и F_SETLKW .

Так что, вероятно, он еще не поддерживается.Для такой простой настройки я бы использовал семафор для сигнализации "память готова".

РЕДАКТИРОВАТЬ

Кажется, я должен упомянуть, что семафоры могут быть созданы с использованием "O_EXCL" (так что естьнет гонок).

...