Выбрать главу

Отличительными моментами flock() являются следующие:

• Блокировка с помощью flock() является вспомогательной; программа, не использующая блокировку, может прийти и испортить без всяких сообщений об ошибках файл, заблокированный с помощью flock().

• Блокируется весь файл. Нет механизма для блокировки только части файла.

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

• Несколько открытых для одного и того же файла дескрипторов используют совместную блокировку. Для удаления блокировки может использоваться любой из них. В отличие от fcntl(), когда нет явного разблокирования, блокировка не удаляется до тех пор, пока не будут закрыты все открытые дескрипторы файла.

• Процесс может иметь лишь одну блокировку файла с помощью flock(); последовательный вызов flock() с двумя различными типами блокировок изменяет тип блокировки на новый.

• На системах GNU/Linux блокировки flock() совершенно независимы от блокировок fcntl(). Многие коммерческие системы Unix реализуют flock() в виде «оболочки» поверх fcntl(), но их семантика различается.

Мы не рекомендуем использовать flock() в новых программах, поскольку ее семантика не такая гибкая и поскольку она не стандартизована POSIX. Поддержка ее в GNU/Linux осуществляется главным образом для обратной совместимости с программным обеспечением, написанным для старых систем BSD Unix.

ЗАМЕЧАНИЕ. Справочная страница GNU/Linux flock(2) предупреждает, что блокировки flock() не работают для смонтированных удаленных файлов. Блокировки fcntl() работают, при условии, что у вас достаточно новая версия Linux и сервер NFS поддерживает блокировки файлов

14.2.4. Обязательная блокировка

Большинство коммерческих систем Unix поддерживают в дополнение к вспомогательной обязательную блокировку файлов. Обязательная блокировка работает лишь с fcntl(). Обязательная блокировка файла контролируется установками прав доступа файла, в частности, путем добавления к файлу бита setgid с помощью команды chmod.

$ echo hello, world > myfile /* Создать файл */

$ ls -l myfile /* Отобразить права доступа */

-rw-r--r-- 1 arnold devel 13 Apr 3 17:11 myfile

$ chmod g+s myfile /* Добавить бит setgid */

$ ls -l myfile /* Показать новые права доступа */

-rw-r-Sr-- 1 arnold devel 13 Apr 3 17:11 myfile

Бит права на исполнение группой должен быть оставлен сброшенным. S показывает, что бит setgid установлен, но что бит права на исполнение — нет; если бы были установлены оба бита, была бы использована строчная буква s.

Комбинация установленного бита setgid и сброшенного бита права на исполнение группой обычно бессмысленно. По этой причине, она была выбрана разработчиками System V для обозначения «использования обязательного блокирования». И в самом деле, добавления этого бита достаточно, чтобы заставить коммерческую систему Unix, такую как Solaris, использовать блокировку файлов.

На системах GNU/Linux несколько другая история. Для обязательных блокировок файл должен иметь установленный бит setgid, но этого одного недостаточно. Файловая система, содержащая файл, также должна быть смонтирована с опцией mand в команде mount.

Мы уже рассмотрели файловые системы, разделы диска, монтирование и команду mount, главным образом, в разделе 8.1 «Монтирование и демонтирование файловых систем». Мы можем продемонстрировать обязательную блокировку с помощью небольшой программы и файловой системой для тестирования на гибком диске. Для начала, вот программа:

1  /* ch14-lockall.c --- Демонстрация обязательной блокировки. */

2

3  #include <stdio.h> /* для fprintf(), stderr, BUFSIZ */

4  #include <errno.h> /* объявление errno */

5  #include <fcntl.h> /* для флагов open() */

6  #include <string.h> /* объявление strerror() */

7  #include <unistd.h> /* для ssize_t */

8  #include <sys/types.h>

9  #include <sys/stat.h> /* для mode_t */

10

11 int

12 main(int argc, char **argv)

13 {

14  int fd;

15  int i, j;

16  mode_t rw_mode;

17  static char message[] = "hello, world\n";

18  struct flock lock;

19

20  if (argc != 2) {

21   fprintf(stderr, "usage: %s file\n", argv[0]);

22   exit(1);

23  }

24

25  rw_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; / * 0644 */

26  fd = open(argv[1], O_RDWR|O_TRUNC|O_CREAT|O_EXCL, rw_mode);

27  if (fd < 0) {

28   fprintf(stderr, "%s: %s: cannot open for read/write: %s\n",

29    argv[0], argv[1], strerror(errno));

30   (void)close(fd);

31   return 1;

32  }

33

34  if (write(fd, message, strlen(message)) != strlen(message)) {

35   fprintf(stderr, "%s: %s: cannot write: %s\n",

36    argv[0], argv[1], strerror(errno));

37   (void)close(fd);

38   return 1;

39  }

40

41  rw_mode |= S_ISGID; /* добавить бит обязательной блокировки */

42

43  if (fchmod(fd, rw_mode) < 0) {

44   fprintf(stderr, "%s: %s: cannot change mode to %o: %s\n",

45    argv[0], argv[1], rw_mode, strerror(errno));

46   (void)close(fd);

47   return 1;

48  }

49

50  /* заблокировать файл */

51  memset(&lock, '\0', sizeof(lock));

52  lock.l_whence = SEEK_SET;

53  lock.l_start = 0;

54  lock.l_len =0; /* блокировка всего файла */

55  lock.l_type = F_WRLCK; /* блокировка записи */

56

57  if (fcntl(fd, F_SETLK, &lock) < 0) {

58   fprintf(stderr, "%s: %s: cannot lock the file: %s\n",

59    argv[0], argv[1], strerror(errno));

60   (void)close(fd);

61   return 1;

62  }

63

64  pause();

65