Операционная система UNIX. Руководство программиста

     

Получение информации о блокировке


Имеется возможность выявить процесс, мешающий установить блокировку. С помощью полученной информации можно узнать, какие бликоровки действуют для файла. Пусть блокировка устанавливается как в предыдущем примере, но при обращении к fcntl(2) используется операция F_GETLK. Если запрашиваемую блокировку установить нельзя, информация о первой мешающей этому блокировке будет возвращена процессу через структуру, адрес которой был передан системному вызову fcntl(2). В результате данные, переданные fcntl(2), будут заменены информацией о противодействующей блокировке. Эта информация включает два ранее не упоминавшихся элемента данных: l_pid и l_sysid. Они используются только операцией F_GETLK. (Для систем, не поддерживающих распределенную архитектуру, значение l_sysid игнорируется.) Два упомянутых поля полностью идентифицируют процесс, установивший блокировку.

Если блокировка, запрашиваемая системным вызовом fcntl(2) с использованием операции F_GETLK, может быть установлена, значением поля l_type становится F_UNLCK, а остальные поля в структуре не изменяются. Используем описанную возможность для вывода информации о всех сегментах файла, заблокированных другими процессами. Заметим, что если на один сегмент файла установлено несколько блокировок, то только первая из них будет найдена.

struct flock lck;

/* Получение и вывод информации о сегментах файла, блокированных на запись */ (void) printf ("ид-р сис. ид-р проц. тип начало длина\n"); lck.l_whence = 0; lck.l_start = 0L; lck.l_len = 0L;

do { lck.l_type = F_WRLCK; (void) fcntl (fd, F_GETLK, &lck); if (lck.l_type != F_UNLCK) { (void) printf("%9d %9d %c %6d %5d\n", lck.l_sysid, lck.l_pid, (lck.l_type == F_WRLCK) ? 'W' : 'R', lck.l_start, lck.l_len); /* Если эта блокировка заведомо покрывает остаток файла, нет нужды выявлять другие блокировки */ if (lck.l_len == 0) break; /* Иначе поищем новую блокировку после найденной */ lck.l_start += lck.l_len; } } while (lck.l_type != F_UNLCK);

Системный вызов fcntl(2) с операцией F_GETLK всегда завершается успешно (то есть процесс не будет приостановлен и вызов не завершится аварийно), если, конечно, значения переданных параметров корректны.


Функция lockf(3C) с операцией F_TEST также может быть использована для выявления процесса, мешающего установить блокировку. Однако эта функция не способна определить заблокированный участок сегмента и то, какой процесс заблокировал его. Программа, использующая lockf(3C) для проверки факта блокировки файла, приведена ниже:

/* Выявление заблокированного сегмента */

/* Установка на начало файла */ (void) lseek (fd, 0, 0L);

/* Нулевой размер тестируемого сегмента означает проверку до конца файла */ if (lockf (fd, F_TEST, 0L) < 0) { switch (errno) { case EACCES: case EAGAIN: (void) printf ("Файл блокирован другим процессом\n"); break; case EBADF: /* Некорректные аргументы lockf */ perror ("lockf"); break; default: (void) printf ( "lockf: непредусмотренная ошибка <%d>\n",errno); break; } }

Процесс, порожденный в результате выполнения системного вызова fork(2), наследует копии дескрипторов файлов, открытых процессом-предком. Кроме того, порождающий и порожденный процессы (для каждого файла) разделяют общий указатель текущей позиции в файле. Если процесс-предок изменит указатель файла, то изменение затронет и порожденный процесс. Такое положение влечет важные следствия при использовании блокировки сегментов. Если значение поля l_whence равно 1, то l_start указывает смещение начала блокируемого сегмента относительно указателя текущей позиции в файле. Если и порождающий, и порожденный процессы блокируют сегменты одного и того же файла, то существует вероятность, что при этом будет использоваться указатель текущей позиции, измененный другим процессом. Эта проблема существует и в случае использования функции lockf(3C) и является следствием требований стандарта /usr/group на блокировку сегментов. Если системный вызов fork(2) используется в программе наряду с блокировкой сегментов, то порожденный процесс при использовании любого метода блокировки должен закрыть и вновь открыть файл. В результате будет создан новый указатель текущей позиции в файле, которым можно будет манипулировать, не сталкиваясь с отмеченной проблемой. Другое решение состоит в том, чтобы использовать системный вызов fcntl(2) со значением поля l_whence 0 или 2, что делает блокировку независимой от указателя текущей позиции.




Содержание раздела