Книга: Основы программирования в Linux

Конкурирующие блокировки

Конкурирующие блокировки

Теперь, когда вы увидели, как проверять существующие блокировки файла, давайте посмотрим, что произойдет, когда две программы состязаются за получение блокировки для одного и того же участка файла. Вы воспользуетесь снова программой lock3 для блокировки файла и новой программой lock5 для попытки установить новую блокировку файла. В завершение вы добавите в программу lock5 несколько вызовов для снятия блокировки (упражнение 7.11).

Упражнение 7.11. Конкурирующие блокировки

Далее приведена программа lock5.с, которая пытается заблокировать уже заблокированные участки файла вместо того, чтобы проверить состояние блокировки других частей файла.

После директив #include и объявлений откройте дескриптор файла.

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
const char *test_file = "/tmp/test_lock";
int main() {
 int file_desc;
 struct flock region_to_lock;
 int res;
 file_desc = open(test_file, O_RDWR | O_CREAT, 0666);
 if (!file_desc) {
  fprintf(stderr, "Unable to open %s for read/writen", test_file);
  exit(EXIT_FAILURE);
 }

В оставшейся части программы задаются разные участки файла, и делается попытка установить для них блокировки разных типов:

 region_to_lock.l_type = F_RDLCK;
 region_to_lock.l_whence = SEEK_SET;
 region_to_lock.l_start = 10;
 region_to_lock.l_len = 5;
 printf("Process %d, trying F_RDLCK, region %d to %dn", getpid(), (int)region_to_lock.l_start,
  (int)(region_to_lock.l_start + region_to_lock.l_len));
 res = fcntl(file_desc, F_SETLK, &region_to_lock);
 if (res == -1) {
  printf("Process %d - failed to lock regionn", getpid());
 } else {
  printf("Process %d — obtained lock regionn", getpid());
 }
 region_to_lock.l_type = F_UNLCK;
 region_to_lock.l_whence = SEEK_SET;
 region_to_lock.l_start = 10;
 region_to_lock.l_len = 5;
 printf("Process %d, trying F_UNLCK, region %d to %dn", getpid(), (int)region_to_lock.l_start,
  (int)(region_to_lock.l_start + region_to_lock.l_len));
 res = fcntl(file_desc, F_SETLK, &region_to_lock);
 if (res == -1) {
  printf("Process %d — failed to unlock regionn", getpid());
 } else {
  printf("Process %d — unlocked regionn", getpid());
 }
 region_to_lock.l_type = F_UNLCK;
 region_to_lock.l_whence = SEEK_SET;
 region_to_lock.l_start = 0;
 region_to_lock.l_len = 50;
 printf("Process %d, trying F_UNLCK, region %d to %dn", getpid()", (int)region_to_lock.l_start,
  (int)(region_to_lock.l_start + region_to_lock.l_len));
 res = fcntl(file_desc, F_SETLK, &region_to_lock);
 if (res == -1) {
  printf("Process %d — failed to unlock regionn", getpid());
 } else {
  printf("Process %d — unlocked regionn", getpid());
 }
 region_to_lock.l_type = F_WRLCK;
 region_to_lock.l_whence = SEEK_SET;
 region_to_lock.lstart = 16;
 region_to_lock.l_len = 5;
 printf("Process %d, trying F_WRLCK, region %d to %dn", getpid(), (int)region_to_lock.l_start,
  (int)(region_to_lock.l_start + region_to_lock.l_len));
 res = fcntl(file_desc, F_SETLK, &region_to_lock);
 if (res == -1) {
  printf("Process %d — failed to lock regionn", getpid());
 } else {
  printf("Process %d — obtained lock on regionn", getpid());
 }
 region_to_lock.l_type = F_RDLCK;
 region_to_lock.l_whence = SEEK_SET;
 region_to_lock.l_start = 40;
 region_to_lock.l_len = 10;
 printf("Process %d, trying F_RDLCK, region %d to %dn", getpid(), (int)region_to_lock.l_start,
  (int)(region_to_lock.l_start + region_to_lock.l_len));
 res = fcntl(filedesc, F_SETLK, &region_to_lock);
 if (res == -1) {
  printf("Process %d — failed to lock regionn", getpid());
 } else {
  printf("Process %d — obtained lock on regionn", getpid());
 }
 region_to_lock.l_type = F_WRLCK;
 region_to_lock.l_whence = SEEK_SET;
 region_to_lock.l_start = 16;
 region_to_lock. l_len = 5;
 printf("Process %d, trying F_WRLCK with wait, region %d to %dn", getpid(), (int)region_to_lock.l_start,
  (int)(region_to_lock.l_start + region_to_lock.l_len));
 res = fcntl(file_desc, F_SETLKW, &region_to_lock);
 if (res == -1) {
  printf("Process %d — failed to lock regionn", getpid());
 } else {
  printf("Process %d — obtained lock, on regionn", getpid());
 }
printf ("Process %d endingn", getpid());
 close(file_desc);
 exit(EXIT_SUCCESS);
}

Если вы сначала запустите программу lock3 в фоновом режиме, далее сразу запускайте новую программу:

$ ./lock3 &
$ process 227 locking file
$ ./lock5

Вы получите следующий вывод:

Process 227 locking file
Process 228, trying F_RDLCK, region 10 to 15
Process 228 — obtained lock on region
Process 228, trying F_UNLCK, region 10 to 15
Process 228 — unlocked region
Process 228, trying F_UNLCK, region 0 to 50
Process 228 — unlocked region
Process 228, trying F_WRLCK, region 16 to 21
Process 228 — failed to lock on region
Process 228, trying F_RDLCK, region 4 0 to 50
Process 228 - failed to lock on region
Process 228, trying F_WRLCK with wait, region 16 to 21
Process 227 closing file
Process 228 — obtained lock on region
Process 228 ending

Как это работает

Сначала программа пытается заблокировать участок с 10-го по 15-й байты с помощью разделяемой блокировки. Эта область уже заблокирована блокировкой того же типа, но одновременные разделяемые блокировки допустимы, и установка блокировки завершается успешно.

Затем программа снимает свою разделяемую блокировку с участка файла, и эта операция тоже завершается успешно. Далее она пытается разблокировать первые 50 байтов файла, даже если у них нет никакой блокировки. Это действие тоже завершается успешно, потому что, несмотря на то, что программа не установила блокировок на этот участок, конечный результат запроса на снятие блокировки заключается в констатации того, что для первых 50 байтов данная программа не поддерживает никаких блокировок.

Далее программа пытается заблокировать участок с 16-то по 21-й байты исключительной блокировкой. Эта область уже заблокирована разделяемой блокировкой, поэтому новая попытка блокировки завершается аварийно, т.к. не может быть создана исключительная блокировка. 

После этого программа пробует установить разделяемую блокировку на участок с 40-го по 50-й байты. Эта область уже заблокирована исключительной блокировкой, поэтому данная операция снова завершается аварийно.

В заключение программа опять пытается получить исключительную блокировку для участка с 16-го по 21-й байты, но в этот раз она применяет команду F_SETLKW, позволяющую ждать до тех пор, пока блокировка не будет установлена. В выводе наступает долгая пауза, длящаяся, пока программа lock3, заблокировавшая этот участок, завершает вызов sleep и закрывает файл, тем самым снимая все установленные блокировки. Программа lock5 возобновляет выполнение, успешно блокирует участок файла и затем тоже завершается.

Оглавление книги


Генерация: 1.291. Запросов К БД/Cache: 3 / 1
поделиться
Вверх Вниз