flock vs fcntl - почему две блокировки одного файла могут вести себя совершенно по-разному
В Linux есть несколько механизмов блокировки файлов. Наиболее часто встречаются flock() и fcntl().
На первый взгляд они делают одно и то же: не дают нескольким процессам одновременно работать с одним файлом.
Но внутри ядра это разные механизмы, поэтому блокировки могут вообще не замечать друг друга.
▪️flock блокирует сам файл
Простейший вариант:
flock /tmp/app.lock -c 'echo "working"; sleep 10'
Пока первый процесс держит блокировку, второй:
flock -n /tmp/app.lock -c 'echo "working"'
получит ошибку и сразу завершится из-за -n.
Часто этот механизм используют для защиты cron-задач:
flock -n /run/myjob.lock /usr/local/bin/myjob
▪️fcntl работает через диапазоны файла
Здесь можно блокировать не только весь файл, но и конкретный диапазон байтов.
На уровне C:
struct flock lock = {
.l_type = F_WRLCK,
.l_whence = SEEK_SET,
.l_start = 0,
.l_len = 100
};
fcntl(fd, F_SETLK, &lock);
Можно заблокировать первые 100 байт, а другой процесс при этом сможет работать с другим диапазоном.
▪️Главная ловушка
Процесс A:
flock /tmp/test.lock
Процесс B использует fcntl() для того же файла.
Они не обязаны конфликтовать.
Причина в том, что flock и традиционные POSIX advisory locks через fcntl исторически используют разные пространства блокировок.
То есть наличие:
flock → locked
не означает автоматически:
fcntl → blocked
И наоборот.
▪️Обе блокировки advisory
Ни flock, ни fcntl сами по себе не запрещают процессу открыть и изменить файл.
Если приложение вообще не проверяет блокировку, оно может спокойно сделать:
echo "data" >> /tmp/file
Поэтому механизм работает только тогда, когда все участники системы договариваются его соблюдать.
▪️Есть ещё одна важная разница
Для flock блокировка связана с открытым file description. При fork() дочерний процесс наследует соответствующую блокировку.
У fcntl семантика другая: POSIX locks связаны с процессом и имеют собственные правила поведения при close().
Из-за этого без понимания модели владения FD легко получить ситуацию, когда один close() неожиданно освобождает блокировку.
▪️Практический вывод
Если нужно просто гарантировать, что cron или несколько экземпляров shell-скрипта не выполняются одновременно:
flock -n /run/myjob.lock /usr/local/bin/myjob
обычно достаточно.
Если приложение должно блокировать отдельные диапазоны файла или ему нужна POSIX-модель блокировок, используют fcntl().
И главное: нельзя считать flock и fcntl взаимозаменяемыми только потому, что оба называются file locking.
BashTex 📱 #bash #utils
В Linux есть несколько механизмов блокировки файлов. Наиболее часто встречаются flock() и fcntl().
На первый взгляд они делают одно и то же: не дают нескольким процессам одновременно работать с одним файлом.
Но внутри ядра это разные механизмы, поэтому блокировки могут вообще не замечать друг друга.
▪️flock блокирует сам файл
Простейший вариант:
flock /tmp/app.lock -c 'echo "working"; sleep 10'
Пока первый процесс держит блокировку, второй:
flock -n /tmp/app.lock -c 'echo "working"'
получит ошибку и сразу завершится из-за -n.
Часто этот механизм используют для защиты cron-задач:
flock -n /run/myjob.lock /usr/local/bin/myjob
▪️fcntl работает через диапазоны файла
Здесь можно блокировать не только весь файл, но и конкретный диапазон байтов.
На уровне C:
struct flock lock = {
.l_type = F_WRLCK,
.l_whence = SEEK_SET,
.l_start = 0,
.l_len = 100
};
fcntl(fd, F_SETLK, &lock);
Можно заблокировать первые 100 байт, а другой процесс при этом сможет работать с другим диапазоном.
▪️Главная ловушка
Процесс A:
flock /tmp/test.lock
Процесс B использует fcntl() для того же файла.
Они не обязаны конфликтовать.
Причина в том, что flock и традиционные POSIX advisory locks через fcntl исторически используют разные пространства блокировок.
То есть наличие:
flock → locked
не означает автоматически:
fcntl → blocked
И наоборот.
▪️Обе блокировки advisory
Ни flock, ни fcntl сами по себе не запрещают процессу открыть и изменить файл.
Если приложение вообще не проверяет блокировку, оно может спокойно сделать:
echo "data" >> /tmp/file
Поэтому механизм работает только тогда, когда все участники системы договариваются его соблюдать.
▪️Есть ещё одна важная разница
Для flock блокировка связана с открытым file description. При fork() дочерний процесс наследует соответствующую блокировку.
У fcntl семантика другая: POSIX locks связаны с процессом и имеют собственные правила поведения при close().
Из-за этого без понимания модели владения FD легко получить ситуацию, когда один close() неожиданно освобождает блокировку.
▪️Практический вывод
Если нужно просто гарантировать, что cron или несколько экземпляров shell-скрипта не выполняются одновременно:
flock -n /run/myjob.lock /usr/local/bin/myjob
обычно достаточно.
Если приложение должно блокировать отдельные диапазоны файла или ему нужна POSIX-модель блокировок, используют fcntl().
И главное: нельзя считать flock и fcntl взаимозаменяемыми только потому, что оба называются file locking.
BashTex 📱 #bash #utils