Текст книги "Основы программирования в Linux"
Автор книги: Нейл Мэтью
Соавторы: Ричард Стоунс
Жанры:
Программирование
,сообщить о нарушении
Текущая страница: 25 (всего у книги 67 страниц)
Блокировка файлов
Блокировка файлов – очень важная составляющая многопользовательских многозадачных операционных систем. Программы часто нуждаются в совместно используемых данных, обычно хранящихся в файлах, и очень важно, что у этих программ есть способ управления файлом. Файл может быть при этом безопасно обновлен или программа может пресечь свои попытки чтения файла, находящегося в переходном состоянии во время записи в него данных другой программой.
У системы Linux есть несколько средств, которые можно применять для блокировки файлов. Простейший способ – блокировка файла на элементарном уровне, когда ничего не может произойти при установленной блокировке. Он предоставляет программе метод создания файлов, обеспечивающий уникальность файла и невозможность одновременного создания этого файла другой программой.
Второй способ более сложный, он позволяет программам блокировать части файла для получения исключительного права доступа к ним. Есть два метода реализации этого варианта блокировки. Мы рассмотрим подробно только один из них, поскольку второй очень похож и отличается от первого немного иным интерфейсом.
Создание файлов с блокировкойМногие приложения нуждаются в возможности создания ресурса в виде файла с блокировкой. Другие программы после этого могут проверить файл, чтобы узнать, есть ли у них право доступах ресурсу.
Как правило, эти заблокированные файлы находятся в специальном месте и имеют имена, связанные с управляемыми ими ресурсами. Например, когда используется модем, система Linux создает файл с блокировкой, часто применяя каталог в каталоге /var/spool.
Помните о том, что блокировки файлов действуют только как индикаторы; программы должны сотрудничать для их применения. Такие блокировки называют рекомендательными (advisory lock), в отличие от обязательных блокировок (mandatory lock), при которых система инициирует блокирование.
Для создания файла с блокировкой (упражнение 7.7) можно использовать системный вызов open
, определенный в файле fcntl.h (уже встречавшемся в предыдущих главах) и содержащий набор флагов O_CREAT
и O_EXCL
. Этот способ позволяет проверить, не существует ли уже такой файл, и затем создать его за одну элементарную неделимую операцию.
Упражнение 7.7. Создание файла с блокировкой
В программе lock1.c вы сможете увидеть файл с блокировкой в действии.
#include
#include
#include
#include
#include
int main() {
int file_desc;
int save_errno;
file_desc = open(«/tmp/LCK.test», O_RDWR | O_CREAT | O_EXCL, 0444);
if (file_desc == -1) {
save errno = errno;
printf(«Open failed with error %dn», save_errno);
} else {
printf(«Open succeededn»);
}
exit(EXIT_SUCCESS);
}
Выполнив программу первый раз, вы получите следующий вывод:
$ ./lock1
Open succeeded
Но при повторной попытке вы получите результат, приведенный далее:
$ ./lock1
Open failed with error 17
Как это работает
Для создания файла с именем /tmp/LCK.test программа выполняет вызов, использующий флаги O_CREAT
и O_EXCL
. Во время первого выполнения программы файл не существует, поэтому вызов open
завершается успешно. Последующие запуски программы завершаются аварийно, потому что файл уже существует. Для успешного выполнения этой программы в дальнейшем вы должны вручную удалить файл с блокировкой.
В системах Linux, ошибка 17 соответствует константе EEXIST
, указывающей на то, что файл уже существует. Номера ошибок определены в файле errno.h или, скорее, в файлах, включаемых этим файлом. В данном случае определение в действительности, находящееся в /usr/include/asm-generic/errno-base.h, гласит
#define EEXIST 17 /* File exists */
Это ошибка, соответствующая аварийному завершению вызова open(O_CREAT | O_EXCL)
.
Если программе на короткий период во время выполнения, часто называемый критической секцией, нужно право исключительного доступа к ресурсу, ей следует перед входом в критическую секцию, создать файл с блокировкой с помощью системного вызова open и применить системный вызов unlink
для удаления этого файла впоследствии, когда она завершит выполнение критической секции.
Вы можете увидеть сотрудничество программ, применяющих этот механизм блокировки, написав программу-пример и запустив одновременно две ее копии (упражнение 7.8). В программе будет использован вызов функции getpid
, с которой вы встречались в главе 4, она возвращает идентификатор процесса, уникальный номер для каждой выполняющейся в данный момент программы.
Упражнение 7.8. Совместная блокировка файлов
1. Далее приведен исходный код тестовой программы lock2.с.
#include
#include
#include
#include
#include
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 presentn», getpid());
sleep(3);
} else {
2. Далее следует критическая секция:
printf(«%d – I have exclusive accessn», getpid());
sleep(1);
(void)close(file_desc);
(void)unlink(lockfile);
3. В этом месте она заканчивается:
sleep(2);
}
}
exit(EXIT_SUCCESS);
}
Для выполнения программы вам сначала нужно выполнить следующую команду, чтобы убедиться в том, что файла не существует:
$ rm -f /tmp/LCK.test2
Затем с помощью приведенной далее команды запустите две копии программы:
$ ./lock2 & ./lock2
Она запускает одну копию программы в фоновом режиме, а вторую – как основную программу. Далее приведен вывод:
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. У него следующее определение:
#include
int fcntl(int fildes, int command, ...);
Системный вызов fcntl
оперирует открытыми дескрипторами файлов и, в зависимости от параметра command
, может выполнять разные задачи. Для блокировки файлов интересны три приведенные далее возможные значения параметра command:
□ F_GETLK
;
□ F_SETLK
;
□ F_SETLKW
.
Когда вы используете эти варианты, третий аргумент в вызове должен быть указателем на структуру struct flock
, поэтому на самом деле прототип вызова выглядит следующим образом:
int fcntl(int fildes, int command, struct flock *flock_structure);
Структура flock
(он англ. file lock) зависит от конкретной реализации, но, как минимум, она будет содержать следующие элементы:
□ short l_type
;
□ short l_whence
;
□ off_t l_start
;
□ off_t l_len
;
□ pid_t l_pid
.
Элемент l_type
принимает одно из нескольких значений (табл. 7.1), определенных в файле fcntl.h.
Таблица 7.1.
F_RDLCK | Разделяемая или совместная блокировка (блокировка на чтение). У разных процессов может быть разделяемая блокировка одних и тех же (или перекрывающихся) участков файла. Если у какого-либо процесса есть разделяемая блокировка, ни один процесс не сможет установить исключительную блокировку этого участка. Для получения совместной блокировки файл должен быть открыт с правом на чтение или на чтение/запись |
F_UNLCK | Разблокировать. Применяется для снятия блокировок |
F_WRLCK | Исключительная блокировка (или блокировка на запись). Только один процесс может установить исключительную блокировку на любой конкретный участок файла. После того как процесс установил такую блокировку, никакой другой процесс не сможет установить блокировку любого типа на этот участок файла. Для установки исключительной блокировки файл должен быть открыт с правом на запись или на чтение/запись |
Элементы l_whence
, l_start
и l_len
определяют участок файла, непрерывную область в байтах. Элемент l_whence
должен задаваться одним из следующих значений: SEEK_SET
, SEEK_CUR
, SEEK_END
(из файла unistd.h). Они соответствуют началу, текущей позиции или концу файла соответственно. Элемент l_whence
задает смещение для первого байта участка файла, определенного элементом l_start
. Обычно оно задается константой SEEK_SET
, поэтому l_start
отсчитывается от начала файла. Параметр l_len
содержит количество байтов в участке файла.
Параметр l_pid
применяется для указания процесса, установившего блокировку; см. следующее далее описание значения F_GETLK
параметра command
.
Для каждого байта в файле может быть установлена блокировка только одного типа в каждый конкретный момент времени и может быть либо разделяемой блокировкой, либо исключительной или блокировка может отсутствовать. Для системного вызова fcntl
существует совсем немного комбинаций значений команд и вариантов, поэтому рассмотрим их все по очереди.
Значение F_GETLK параметра command
Первое значение параметра command – F_GETLK
. Эта команда получает информацию о файле, который открыт fildes
(первый параметр в вызове). Она не пытается блокировать файл. В процессе вызова передаются сведения о типе блокировки, которую хотелось бы установить, и вызов fcntl
с командой F_GETLK
возвращает любую информацию, которая могла бы помешать установке блокировки.
Значения, используемые в структуре flock
, приведены в табл. 7.2.
Таблица 7.2
l_type | Или F_RDLCK для разделяемой (только чтение) блокировки, или F_WRLCK для исключительной (на запись) блокировки |
l_whence | Одно из значений: SEEK_SET , SEEK_CUR или SEEK_END LCK |
l_start | Начальный байт интересующего вас участка файла |
l_len | Количество байтов в интересующем вас участке файла |
l_pid | Идентификатор процесса, удерживающего блокировку |
Процесс может применять вызов с командой F_GETLK
для определения текущего состояния блокировки участка файла. Он должен настроить структуру flock
, указав тип требуемой блокировки и определив интересующую его область файла. Вызов fcntl
возвращает в случае успешного завершения значение, отличное от -1. Если у файла уже есть блокировки, препятствующие установке требуемой блокировки, структура flock
обновляется соответствующими данными. Если блокировке ничто не мешает, структура flock
не изменяется. Если вызов с командой F_GETLK
не может получить информацию, он возвращает -1 для обозначения аварийного завершения.
Если вызов с командой F_GETLK
завершился успешно (т. е. вернул значение, отличное от -1), вызвавшее его приложение должно проверить, изменено ли содержимое структуры flock
. Поскольку значение l_pid
содержит идентификатор блокирующего процесса (если таковой найден), это поле очень удобно для того, чтобы проверить, изменялась ли структура flock
.
Значение F_SETLK параметра command
Эта команда пытается заблокировать или разблокировать участок файла, заданного fildes
. В табл. 7.3 приведены значения полей структуры flock
(отличающиеся от значений, применяемых командой F_GETLK
).
Таблица 7.3
l_type | Одно из следующих: • F_RDLCK – для разделяемой или допускающей только чтение блокировки; • F_WRLCK – для исключительной или блокировки записи; • F_UNLCK – для разблокирования участка |
l_pid | Не используется |
Как и в случае F_GETLK
, блокируемый участок определяется значениями элементов l_start
, l_whence
и l_len
структуры flock
. Если блокировка установлена, вызов fcntl
вернет значение, отличное от -1, при аварийном завершении возвращается -1. Вызов завершается немедленно.
Значение F_SETLKW параметра command
Команда F_SETLKW
аналогична команде F_SETLK
за исключением того, что при невозможности установки блокировки вызов будет ждать до тех пор, пока такая возможность не представится. После перехода в состояние ожидания вызов завершится только, когда блокировка будет установлена или появится сигнал. Сигналы мы обсудим в главе 11.
Все блокировки файла, установленные программой, автоматически очищаются, когда закрывается соответствующий дескриптор файла. То же самое происходит, когда программа завершается.
Применение вызовов read и write при наличии блокировкиКогда вы применяете блокировку участков файла, очень важно использовать для доступа к данным низкоуровневые вызовы read
и write
вместо высокоуровневых функций fread
и fwrite
. Это необходимо, поскольку функции fread
и fwrite
выполняют внутри библиотеки буферизацию читаемых или записываемых данных, так что при выполнений вызова fread
для считывания 100 байтов из файла может быть (и на самом деле почти наверняка будет), считано более 100 байтов, и дополнительные данные помещаются во внутрибиблиотечный буфер. Если программа применит функцию fread
для считывания следующих 100 байтов, она на самом деле считает данные из буфера и не разрешит низкоуровневому вызову read
извлечь больше данных из файла.
Для того чтобы понять, в чем тут проблема, рассмотрим две программы, которые хотят обновить один и тот же файл. Предположим, что файл содержит 200 байтов данных, все нули. Первая программа начинает работу и устанавливает блокировку на запись для первых 100 байтов файла. Затем она применяет функцию fread
для считывания этих 100 байтов. Однако, как было показано в одной из предшествующих глав, fread
будет каждый раз считывать больше, до BUFSIZ
байтов, поэтому она на самом деле считает в память целиком весь файл, но программе вернет только первые 100 байтов.
Затем стартует вторая программа. Она устанавливает блокировку write
на вторые 100 байтов файла. Это действие завершится успешно, поскольку первая программа заблокировала только первые 100 байтов. Вторая программа записывает двойки в байты с 100-го по 199-й, закрывает файл, снимает блокировку и завершается. В это время первая программа блокирует вторые 100 байтов файла и вызывает функцию fread
для их считывания. Поскольку эти данные были уже занесены библиотекой в буфер, программа увидит 100 байтов нулей, а не 100 двоек, которые на самом деле хранятся в файле на жестком диске. Подобной проблемы не возникает, если вы применяете вызовы read
и write
.
Приведенное описание блокировки файла может показаться сложноватым, но ее труднее описать, чем применить. Поэтому выполните упражнение 7.9.
Упражнение 7.9. Блокировка файла с помощью вызова fcntl
Давайте рассмотрим пример работы блокировки файла в программе lock3.с. Для опробования блокировки вам понадобятся две программы: одна для установки блокировки и другая для ее тестирования. Первая программа выполняет блокировку.
1. Начните с файлов include
и объявлений переменных:
#include
#include
#include
#include
const char *test_file = «/tmp/test_lock»;
int main() {
int file desc;
int byte_count;
char *byte_to_write = "A";
struct flock region_1;
struct flock region_2;
int res;
2. Откройте файловый дескриптор:
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);
}
3. Поместите данные в файл:
for (byte_count = 0; byte_count < 100; byte_count++) {
(void)write(file_desc, byte_to_write, 1);
}
4. Задайте разделяемую блокировку для участка region 1 с 10-го байта по 30-й:
region_1.l_type = F_RDLCK;
region_1.l_whence = SEEK_SET;
region_1.l_start = 10;
region_1.l_len = 20;
5. Задайте исключительную блокировку для участка region_2 с 40-го байта по 50-й:
region_2.l_type = F_WRLCK;
region_2.l_whence = SEEK_SET;
region_2.l_start = 40;
region_2.l_len = 10;
6. Теперь заблокируйте файл:
printf(«Process %d locking filen», getpid());
res = fcntl(file_desc, F_SETLK, ®ion_1);
if (res == -1) fprintf(stderr, «Failed to lock region 1n»);
res = fcntl(file_desc, F_SETLK, ®ion_2);
if (res = fprintf(stderr, «Failed to lock region 2n»);
7. Подождите какое-то время:
sleep(60);
printf («Process %d closing filen», getpid());
close(file_desc);
exit(EXIT_SUCCESS);
}
Как это работает
Сначала программа создает файл, открывает его для чтения и записи и затем заполняет файл данными. Далее задаются два участка: первый с 10-го по 30-й байт для разделяемой блокировки и второй с 40-го по 50-й байт для исключительной блокировки. Затем программа выполняет вызов fcntl
для установки блокировок на два участка файла и ждет в течение минуты, прежде чем закрыть файл и завершить работу.
На рис. 7.1 показан этот сценарий с блокировками в тот момент, когда программа переходит к ожиданию.
Рис. 7.1
Сама по себе эта программа не очень полезна. Вам нужна вторая программа lock4.c для тестирования блокировок (упражнение 7.10).
Упражнение 7.10. Тестирование блокировок файла
В этом примере вы напишете программу, проверяющую блокировки разных типов, установленные для различных участков файла.
1. Как обычно, начнем с заголовочных файлов и объявлений:
#include
#include
#include
#include
const char *test_file = «/tmp/test_lock»;
#define SIZE_TO_TRY 5
void show_lock_info(struct flock *to_show);
int main() {
int file_desc;
int res;
struct flock region_to_test;
int start_byte;
2. Откройте дескриптор файла:
file_desc = open(test_file, O_RDWR | O_CREAT, 0666);
if (!file_desc) {
fprintf(stderr, «Unable to open %s for read/write», test_file);
exit(EXIT_FAILURE);
}
for (start_byte = 0; start_byte < 99; start_byte += SIZE_TO_TRY) {
3. Задайте участок файла, который хотите проверить:
region_to_test.l_type = F_WRLCK;
region_to_test.l_whence = SEEK_SET;
region_to_test.lstart = start_byte;
region_to_test.l_len = SIZE_TO_TRY;
region_to_test.l_pid = -1;
printf(«Testing F_WRLCK on region from %d to %dn», start_byte, start_byte + SIZE_TO_TRY);
4. Теперь проверьте блокировку файла:
res = fcntl(file_desc, F_GETLK, ®ion_to_test);
if (res == -1) {
fprintf(stderr, «F_GETLK failedn»);
exit(EXIT_FAILURE);
}
if (region_to_test.l_pid != -1) {
printf(«Lock would fail. F_GETLK returned:n»);
showlockinfo(®ion_to_test);
} else {
printf(«F_WRLCK – Lock would succeedn»);
}
5. Далее повторите тест с разделяемой блокировкой (на чтение). Снова задайте участок файла, который хотите проверить:
region_to_test.l_type = F_RDLCK;
region_to_test.l_whence = SEEK_SET;
region_to_test.l_start = start_byte;
region_to_test.l_len = SIZE_TO_TRY;
region_to_test.l_pid = -1;
printf(«Testing F_RDLCK on region from %d to %dn», start_byte, start_byte + SIZE_TO_TRY);
6. Еще раз проверьте блокировку файла:
res = fcntl(file_desc, F_GETLK, ®ion_to_test);
if (res == -1) {
fprintf(stderr, «F_GETLK failedn»);
exit(EXIT_FAILURE);
}
if (region_to_test.l_pid != -1) {
printf(«Lock would fail. F_GETLK returned:n»);
show_lock_info(®ion_to_test);
} else {
printf(«F_RDLCK – Lock would succeedn»);
}
}
close(file_desc);
exit(EXIT_SUCCESS);
}
void show_lock_info(struct flock *to_show) {
printf("tl_type %d, ", to_show->l_type);
printf("l_whence %d, ", to_show->l_whence);
printf("l_start %d, (int)to_show->l_start);
printf("l_len %d, ", (int)to_show->l_len);
printf(«l_pid %dn», to_show->l_pid);
}
Для проверки блокировки сначала запустите программу lock3, затем выполните программу lock4, чтобы протестировать заблокированный файл. Сделайте это, запустив программу lock3 в фоновом режиме с помощью следующей команды:
$ ./lock3 &
$ process 1534 locking file
На экране появится приглашение для ввода команд, поскольку lock3 выполняется в фоновом режиме. Далее сразу же запустите программу lock4 с помощью следующей команды:
$ ./lock4
Вы получите вывод, приведенный далее с некоторыми пропусками для краткости:
Testing F_WRLCK on region from 0 to 5
F_WRLCK – Lock would succeed
Testing F_RDLCK on region from 0 to 5
F_RDLCK – Lock would succeed
...
Testing F_WRLCK on region from 10 to 15
Lock would fail. F_GETLK returned:
l_type 0, l_whence 0, l_start 10, l_len 20, l_pid 1534
Testing F_RDLCK on region from 10 to 15
F_RDLCK – Lock would succeed
Testing F_WRLCK on region from 15 to 20
Lock would fail. F_GETLK returned:
l_type 0, l_whence 0, l_start 10, l_len 20, l_pid 1534
Testing F_RDLCK on region from 15 to 20
F_RDLCK – Lock would succeed
...
Testing F_WRLCK on region from 25 to 30
Lock would fail. F_GETLK returned:
l_type 0, l_whence 0, l_start 10, l_len 20, l_pid 1534
Testing F_RDLCK on region from 25 to 30
F_RDLCK – Lock would succeed
...
Testing F_WRLCK on region from 40 to 45
Lock would fail. F_GETLK returned:
l_type 1, l_whence 0, l_start 40, l_len 10, l_pid 1534
Testing F_RDLCK on region from 40 to 45
Lock would fail. F_GETLK returned:
l_type 1, l_whence 0, l_start 40, l_len 10, l_pid 1534
...
Testing F_RDLCK on region from 95 to 100
F_RDLCK – Lock would succeed
Как это работает
Для каждой группы из пяти байтов в файле программа lock4 задает структуру участка файла для тестирования блокировок, которую она потом применяет для определения того, может ли этот участок быть заблокирован для чтения или записи. Возвращаемая информация показывает байты, относящиеся к участку файла, смещение от нулевого байта, которое могло бы вызвать аварийное завершение запроса на блокировку. Поскольку поле l_pid
возвращаемой структуры содержит идентификатор программы, владеющей в данный момент заблокированным файлом, программа задает ему значение -1 (некорректное значение) и затем проверяет, изменилось ли оно после завершения вызова fcntl
. Если участок в данный момент не заблокирован, поле l_pid
не изменится.
Для того чтобы понять вывод, следует заглянуть в заголовочный файл fcntl.h (обычно /usr/include/fcntl.h) и увидеть, что поле l_type
, равное 1, вытекает из определения F_WRLCK
как 1, а равное 0 из определения F_RDLCK
как 0. Таким образом, поле l_type
, равное 1, говорит о том, что блокировка не будет установлена, поскольку существует блокировка на запись, а поле l_type
, равное 0, свидетельствует о существовании блокировки на чтение. Для тех участков файла, которые не заблокировала программа lock3, могут быть установлены и разделяемая, и исключительная блокировки.
Для байтов с 10-го по 30-й возможна установка разделяемой блокировки, поскольку блокировка, установленная программой lock3, не исключительная, а разделяемая. Для участка с 40-го по 50-й байт нельзя установить оба типа блокировки, поскольку lock3 задала исключительную (F_WRLCK
) блокировку для этого участка.
После завершения программы lock4 необходимо немного подождать, чтобы программа lock3 завершила вызов sleep
и закончила выполнение.