Блокировка файла прикрепляется к файлу через описание файла . На высоком уровне последовательность операций в одном экземпляре скрипта:
- Откройте файл, к которому прикреплена блокировка («файл блокировки»).
- Взять блокировку на файл блокировки.
- Делай вещи.
- Закройте файл блокировки. Это снимает блокировку, которая прикреплена к описанию файла, созданному путем открытия файла.
Удержание блокировки не позволяет запустить еще одну копию того же сценария, потому что это то, что делают блокировки. Пока в системе существует исключительная блокировка файла, невозможно создать второй экземпляр той же блокировки, даже с помощью другого описания файла.
Открытие файла создает описание файла . Это объект ядра, который не имеет прямой видимости в интерфейсах программирования. Вы обращаетесь к описанию файла косвенно через файловые дескрипторы, но обычно вы думаете о нем как о доступе к файлу (чтение или запись его содержимого или метаданных). Блокировка - это один из атрибутов, которые являются свойством описания файла, а не файла или дескриптора.
В начале, когда файл открывается, описание файла имеет один дескриптор файла, но можно создать больше дескрипторов либо путем создания другого дескриптора ( dup
семейства системных вызовов), либо путем разветвления подпроцесса (после чего родительский и ребенок имеет доступ к тому же описанию файла). Дескриптор файла может быть закрыт явно или когда процесс, в котором он находится, умирает. Когда последний дескриптор файла, прикрепленный к файлу, закрывается, описание файла закрывается.
Вот как последовательность операций выше влияет на описание файла.
- Перенаправление
<$0
открывает файл сценария в подоболочке, создавая описание файла. На данный момент к описанию прикреплен единственный дескриптор файла: дескриптор номер 0 в подоболочке.
- Subshell вызывает
flock
и ждет его выхода. Во время работы flock к описанию прикреплены два дескриптора: номер 0 в подоболочке и номер 0 в процессе стека. Когда flock берет блокировку, это устанавливает свойство описания файла. Если другое описание файла уже имеет блокировку файла, flock не может взять блокировку, так как это эксклюзивная блокировка.
- Раковина делает вещи. Поскольку в описании по-прежнему имеется дескриптор открытого файла с описанием с блокировкой, это описание сохраняется и сохраняет свою блокировку, поскольку никто никогда не снимает блокировку.
- Подоболочка умирает в закрывающей скобке. Это закрывает последний дескриптор файла в описании файла, который имеет блокировку, поэтому блокировка исчезает в этот момент.
Причина, по которой сценарий использует перенаправление, $0
заключается в том, что перенаправление является единственным способом открыть файл в оболочке, а сохранение активного перенаправления является единственным способом сохранить дескриптор файла открытым. Подоболочка никогда не читает со своего стандартного ввода, она просто должна оставаться открытой. На языке, который дает прямой доступ к открытию и закрытию вызова, вы можете использовать
fd = open($0)
flock(fd, LOCK_EX)
do stuff
close(fd)
На самом деле вы можете получить ту же последовательность операций в оболочке, если вы делаете перенаправление с помощью exec
встроенной функции.
exec <$0
flock -n -x 0
# do stuff
exec <&-
Сценарий может использовать другой файловый дескриптор, если он хочет сохранить доступ к исходному стандартному вводу.
exec 3<$0
flock -n -x 0
# do stuff
exec 3<&-
или с ракушкой:
(
flock -n -x 3
# do stuff
) 3<$0
Блокировка не должна быть в файле сценария. Это может быть любой файл, который может быть открыт для чтения (поэтому он должен существовать, это должен быть тип файла, который можно прочитать, например обычный файл или именованный канал, но не каталог, и процесс сценария должен иметь разрешение на его чтение). Преимущество файла сценария в том, что он гарантированно присутствует и доступен для чтения (за исключением крайнего случая, когда он был удален извне между моментом вызова сценария и моментом, когда сценарий достигает <$0
перенаправления).
Пока это flock
удается, а сценарий находится в файловой системе, где блокировки не содержат ошибок (некоторые сетевые файловые системы, такие как NFS, могут содержать ошибки), я не вижу, как использование другого файла блокировки может привести к состоянию гонки. Я подозреваю ошибку манипуляции с вашей стороны.