#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
const char *lock_file = "/tmp/LCK.test2";
int main() {
int file_desc;
int tries = 10;
while (--tries) {
file_desc = open(lock_file, O_RDWR | O_CREAT | O_EXCL, 0444);
if (file_desc == -1) {
printf("%d - Lock already present\n", getpid());
sleep(3);
} else {
2. Далее следует критическая секция:
printf("%d — I have exclusive access\n", getpid());
sleep(1);
(void)close(file_desc);
(void)unlink(lockfile);
3. В этом месте она заканчивается:
sleep(2);
}
}
exit(EXIT_SUCCESS);
}
Для выполнения программы вам сначала нужно выполнить следующую команду, чтобы убедиться в том, что файла не существует:
$ <b>rm -f /tmp/LCK.test2</b>
Затем с помощью приведенной далее команды запустите две копии программы:
$ <b>./lock2 & ./lock2</b>
Она запускает одну копию программы в фоновом режиме, а вторую — как основную программу. Далее приведен вывод:
1284 — I have exclusive access
1283 — Lock already present
1283 — I have exclusive access
1284 — Lock already present
1284 — I have exclusive access
1283 — Lock already present
1283 — I have exclusive access
1284 — Lock already present
1284 — I have exclusive access
1283 — Lock already present
1283 — I have exclusive access
1284 — Lock already present
1284 — I have exclusive access
1283 — Lock already present
1283 — I have exclusive access
1284 — Lock already present
1284 — I have exclusive access
1283 — Lock already present
1283 — I have exclusive access
1284 — Lock already present
В приведенном примере показано, как взаимодействуют две выполняющиеся копии одной и той же программы. Если вы попробуете выполнить данный пример, то почти наверняка увидите другие идентификаторы процессов в выводе, но поведение программ будет тем же самым.
Как это работает
Для демонстрации вы 10 раз выполняете в программе цикл с помощью оператора
while
. Затем программа пытается получить доступ к дефицитному ресурсу, создав уникальный файл с блокировкой /tmp/LCK.test2. Если эта попытка терпит неудачу из-за того, что файл уже существует, программа ждет короткий промежуток времени и затем снова пытается создать файл. Если ей это удается, она получает доступ к ресурсу и в части программы, помеченной как "критическая секция", выполняет любую обработку, требующую исключительных прав доступа.
Поскольку это всего лишь пример, вы ждете очень короткий промежуток времени. Когда программа завершает использование ресурса, она снимает блокировку, удаляя файл с блокировкой. Далее она может выполнить другую обработку (в данном случае это просто функция
sleep
) прежде, чем попытаться возобновить блокировку. Файлы с блокировкой действуют как двоичный семафор, давая программе ответ "да" или "нет" на вопрос: "Могу ли я использовать ресурс?". В
главе 14 вы узнаете больше о семафорах.
Примечание
Важно уяснить, что это совместное мероприятие, и вы должны корректно писать программы для его работы. Программа, потерпевшая неудачу в создании файла с блокировкой, не может просто удалить файл и попробовать снова, Возможно, в дальнейшем она сумеет создать файл с блокировкой, но у другой программы, уже создавшей такой файл, нет способа узнать о том, что она лишилась исключительного доступа к ресурсу.
Блокировка участков файла
Создание файлов с блокировкой подходит для управления исключительным доступом к ресурсам, таким как последовательные порты или редко используемые файлы, но этот способ не годится для доступа к большим совместно используемым файлам. Предположим, что у вас есть большой файл, написанный одной программой и одновременно обновляемый многими программами. Такая ситуация может возникнуть, если программа записывает какие-то данные, получаемые непрерывно или в течение длительного периода, и обрабатывает их с помощью нескольких разных программ. Обрабатывающие программы не могут ждать, пока программа, записывающая данные, завершится — она работает постоянно, поэтому программам нужен какой-то способ кооперации для обеспечения одновременного доступа к одному и тому же файлу.
Урегулировать эту ситуацию можно, блокируя участки файла. При этом конкретная часть файла блокируется, но другие программы могут иметь доступ к другим участкам файла. Это называется блокировкой сегментов или участков файла. У системы Linux есть (как минимум) два способа сделать это: с помощью системного вызова
fcntl
или системного вызова
lockf
. Мы рассмотрим интерфейс
fcntl
, поскольку он наиболее часто применяется. Интерфейс
lockf
в основном аналогичен, и в ОС Linux он используется как альтернативный интерфейсу
fcntl
. Однако блокирующие механизмы
fcntl
и
lockf
не работают вместе: у них разные низкоуровневые реализации. Поэтому никогда не следует смешивать вызовы этих двух типов; выберите один или другой.
Вы встречали вызов fcntl в главе 3. У него следующее определение:
<b>#include <fcntl.h></b>
<b>int fcntl(int fildes, int command, ...);</b>
Системный вызов
fcntl
оперирует открытыми дескрипторами файлов и, в зависимости от параметра
command
, может выполнять разные задачи. Для блокировки файлов интересны три приведенные далее возможные значения параметра command: