LINUX.ORG.RU

Как сделать, чтоб попытка exclusive flock не давала делать shared flock

 


0

2

Несколько процессов могут читать из файла одновременно. Достаточно редко происходит запись в файл, при этом другие процессы из него читать не должны (ну и писать тоже).

Пытаюсь организовать блокировку с помощью flock. И вот какая штука получается: когда стоит shared lock, начинаем делать exclusive lock, соответственно происходит ожидание пока shared lock пропадёт. Но засада в том, что в это время другой процесс может сделать свой shared lock, и его тоже придётся ждать.

Как бы сделать так чтоб когда начали делать exclusive lock другие процессы ждали прежде чем получить shared lock?

Вот код для наглядности:

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/types.h>

int open_lockme() {
  int fd = open("lockme.tmp", O_CREAT);
  if (fd == -1) {
    perror("failed to open \"lockme.tmp\"");
    exit(EXIT_FAILURE);
  }
  return fd;
}

main() {
  if (fork() == 0) {
    if (fork() == 0) {
      int fd = open_lockme(); puts("(1): trying to set shared lock");
      flock(fd, LOCK_SH);     puts("(1): shared lock set");
      sleep(3);
      close(fd);              puts("(1): fd closed, shared lock removed");
    }
    else {
      int fd = open_lockme();
      sleep(2);           puts("(2): trying to set shared lock");
      flock(fd, LOCK_SH); puts("(2): shared lock set");
      sleep(2);
      close(fd);          puts("(2): fd closed, shared lock removed");
    }
  }
  else {
    int fd = open_lockme();
    sleep(1);           puts("(3): trying to set exclusive lock");
    flock(fd, LOCK_EX); puts("(3): exclusive lock set");
    sleep(2);
    close(fd);          puts("(3): fd closed, exclusive lock removed");
  }
  return EXIT_SUCCESS;
}
Хочется чтоб (2) ждал (3), а не наоборот.

А если проверить при помощи fcntl? Если не заблокирован эксклюзивно, делать разделяемую блокировку... Или даже тупо при помощи F_SETLK сразу ставить блокировку — если fcntl вернет -1, значит, файл уже заблокирован.

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от Eddy_Em

А если проверить при помощи fcntl?

flock(2)

… there is no interaction between the types of lock placed by flock() and fcntl(2) …

Если вместо flock использовать fcntl, там другая беда:

fcntl(2)

If a conflicting lock is held by another process, this call returns -1 …

То есть он не ждёт, не говоря уже о запрете новых read локов.

LinuxUser ★★★
() автор топика
Ответ на: комментарий от Eddy_Em

он не ждёт

А кто тебе мешает ждать самому?

Ничего. Это не главное, главное вот:

не ждёт, не говоря уже о запрете новых read локов

Ждёт и flock, но пока он ждёт могут делаться новые read/shared локи.
А я хочу чтоб их нельзя было делать.

LinuxUser ★★★
() автор топика
Ответ на: комментарий от LinuxUser

Надо с вспомогательным файлом.
Схема.

    fd  -основной файл
    fd0 -вспомогательный блокировочный файл

    // читатель
    lock_wr(fd0);
    unlock(fd0);
    lock_rd(fd);
    read(fd);
    unlock(fd);

    // писатель
    lock_wr(fd0);
    lock_wr(fd);
    write(fd);
    unlock(fd);
    unlock(fd0);

oleg_2
()
Ответ на: комментарий от oleg_2

У тебя в коде ошибка — у читателя unlock(fd0) надо в самый конец перенести, но всё равно спасибо.

Я сделал с бинарным семафором.
Вызовы flock обернул в занимание и освобождение семафора.
Таким образом никакая сволочь не сделает шаред лок, пока мы ждём эксклузив лок :)

#include <errno.h>
#include <fcntl.h>
#include <semaphore.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/types.h>

#define SEMAPHORE_NAME "/{1f99c438-07c8-4be2-8cf2-0593fcd49cd7}"

sem_t *sem;
int fd;

void open_lockme() {
  fd = open("lockme.tmp", O_CREAT);
  if (fd == -1) {
    perror("failed to open \"lockme.tmp\"");
    exit(EXIT_FAILURE);
  }
}

int lock_sh(int i) {
  sem_wait(sem);                   printf("  (%i): sem_wait(sem); ends\n", i);
  int result = flock(fd, LOCK_SH); printf("  (%i): flock(fd, LOCK_SH); ends\n", i);
  sem_post(sem);                   printf("  (%i): sem_post(sem); ends\n", i);
  return result;
}

int lock_ex(int i) {
  sem_wait(sem);                   printf("  (%i): sem_wait(sem); ends\n", i);
  int result = flock(fd, LOCK_EX); printf("  (%i): flock(fd, LOCK_EX); ends\n", i);
  sem_post(sem);                   printf("  (%i): sem_post(sem); ends\n", i);
}

main() {
  sem = sem_open(SEMAPHORE_NAME, O_CREAT, 0700, 1);
  if (sem == SEM_FAILED) return EXIT_FAILURE;

  if (fork() == 0) {
    if (fork() == 0) {
      open_lockme();           puts("(1): trying to set shared lock");
      if (lock_sh(1) == -1)    perror("(1): shared lock failed");
      else {                   puts("(1): shared lock set");
      sleep(3); }
      close(fd);               puts("(1): fd closed");
    }
    else {
      open_lockme();
      sleep(2);              puts("(2): trying to set shared lock");
      if (lock_sh(2) == -1)  perror("(2): shared lock failed");
      else {                 puts("(2): shared lock set");
      sleep(2); }
      close(fd);             puts("(2): fd closed");
    }
  }
  else {
    open_lockme();
    sleep(1);              puts("(3): trying to set exclusive lock");
    if (lock_ex(3) == -1)  perror("(3): exclusive lock failed");
    else {                 puts("(3): exclusive lock set");
    sleep(2); }
    close(fd);             puts("(3): fd closed");
  }
  return EXIT_SUCCESS;
}

LinuxUser ★★★
() автор топика
Ответ на: комментарий от LinuxUser

Не. Что-то неладно. По-моему, так не работает shared.
Т. е. все блокировки эксклюзивные. Надо протестить. У меня нет возможности.
И в моей схеме вроде нет ошибки. Если unlock(fd0) перенести в самый конец,
то это будет чистая эксклюзивная блокировка, зачем-то дублированная еще и
шареной.
Схема так работает.
// читатель
lock_wr(fd0);
unlock(fd0);
проверяет, не заблокирован ли fd0, если да, то подвисает до освобождения.
fd0 может быть заблокирован другим читателем, но это ненадолго. Или писателем,
это надолго.

oleg_2
()
Ответ на: комментарий от oleg_2

lock_wr -эксклюзивная блокировка
lock_rd -общая блокировка на чтение

oleg_2
()
Ответ на: комментарий от oleg_2

Да ты прав, у тебя ошибки нет. У меня, впринципе тоже в способе блокировки ошибки нет. Только в int lock_sh(int i) сразу после sem_wait(sem); можно делать sem_post(sem);, это теоретически немного увеличит скорость работы программы.

Ошибка в есть при открытии файла:

fd = open("lockme.tmp", O_CREAT);
надо заменить на
fd = open("lockme.tmp", O_CREAT, S_IRUSR | S_IWUSR);

LinuxUser ★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.