Блокировка файлов
Есть несколько способов блокировки файла. Конкретный выбор зависит, в частности, от того, как оставшаяся часть программы будет использовать блокировку. Нужно также учесть проблемы эффективности и мобильности программы. Далее будут описаны два метода: один использует системный вызов fcntl(2), другой - библиотечную функцию lockf(3C) (удовлетворяющую стандарту /usr/group).
Блокировка всего файла на самом деле является частным случаем блокировки сегмента, поскольку и концепция, и результат блокировки одинаковы. Файл блокируется начиная с байта со смещением ноль и кончая максимально возможным размером файла, что нужно для предотвращения блокировок каких-либо сегментов файла. В этом случае значение длины блокировки устанавливается в ноль. Далее приведен фрагмент исходного текста, использующего для достижения цели системный вызов fcntl(2):
#include <fcntl.h> . . . #define MAX_TRY 10 int try; struct flock lck;
try = 0;
/* Заполним структуру, нужную для блокировки сегмента. Адрес структуры передается системному вызову fcntl */
lck.l_type = F_WRLCK; /* Блокировка на запись */ lck.l_whence = 0; /* Смещение от начала будет равно l_start */ lck.l_start = 0L; /* Блокировка от начала файла */ lck.l_len = 0L; /* и до конца */
/* Делаем не более MAX_TRY попыток блокировки */
while (fcntl (fd, F_SETLK, &lck) < 0) { if (errno == EAGAIN errno == EACCES) { /* Могут быть и другие ошибки, при которых надо повторить попытку */ if (++try < MAX_TRY) { (void) sleep (2); continue; } (void) fprintf (stderr, "Файл занят\n"); return; } perror("fcntl"); exit(2); } . . .
Приведенная часть исходного текста пытается заблокировать файл. Попытки повторяются несколько раз до тех пор, пока не случится одно из событий:
- Файл удалось заблокировать.
- Возникла ошибка.
- Попытки прекращены из-за того, что их количество превысило MAX_TRY.
Чтобы выполнить эту же задачу, используя функцию lockf(3C), исходный текст должен иметь следующий вид:
#include <unistd.h> #define MAX_TRY 10 int try; try = 0;
/* Устанавливаем указатель текущей позиции в начало файла */
lseek (fd, 0L, 0);
/* Делаем не более MAX_TRY попыток блокировки */
while (lockf (fd, F_TLOCK, 0L) < 0) { if (errno == EAGAIN errno == EACCES) { /* Могут быть и другие ошибки, при которых надо повторить попытку */ if (++try < MAX_TRY) { (void) sleep (2); continue; } (void) fprintf (stderr, "Файл занят\n"); return; } perror("lockf"); exit(2); } . . .
Надо заметить, что пример с использованием lockf(3C) выглядит проще, однако пример с fcntl(2) демонстрирует дополнительную гибкость. При использовании fcntl(2) можно установить тип блокировки и начало блокируемого сегмента просто путем присваивания нужных значений компонентам структуры. В случае lockf(3C) просто устанавливается блокировка на запись (монопольная); для того, чтобы указать начало блокируемого сегмента, нужен дополнительный системный вызов [lseek(2)].