19 августа 2013 года Рэндал Л. Шварц опубликовал этот сценарий оболочки, который должен был гарантировать, что в Linux «работает только один экземпляр []], без условий гонки или необходимости очистки файлов блокировки»:
#!/bin/sh
# randal_l_schwartz_001.sh
(
if ! flock -n -x 0
then
echo "$$ cannot get flock"
exit 0
fi
echo "$$ start"
sleep 10 # for testing. put the real task here
echo "$$ end"
) < $0
Кажется, работает как рекламируется:
$ ./randal_l_schwartz_001.sh & ./randal_l_schwartz_001.sh
[1] 11863
11863 start
11864 cannot get flock
$ 11863 end
[1]+ Done ./randal_l_schwartz_001.sh
$
Вот что я понимаю:
- Сценарий перенаправляет (
<
) копию своего собственного содержимого (т.$0
Е. Из ) в STDIN (т. Е. Файловый дескриптор0
) подоболочки. - Внутри подоболочки сценарий пытается получить неблокирующую монопольную блокировку (
flock -n -x
) для файлового дескриптора0
.- Если эта попытка не удалась, подоболочка завершается (как и основной сценарий, так как ему больше нечего делать).
- Если попытка вместо этого удалась, подоболочка запускает нужную задачу.
Вот мои вопросы:
- Почему сценарий должен перенаправлять в файловый дескриптор, унаследованный подоболочкой, копию своего собственного содержимого, а не, скажем, содержимого какого-либо другого файла? (Я попытался перенаправить из другого файла и запустить снова, как описано выше, и порядок выполнения изменился: неосновная задача получила блокировку до фоновой. Поэтому, возможно, использование собственного содержимого файла позволяет избежать условий гонки; но как?)
- Зачем скрипту нужно перенаправить в дескриптор файла, унаследованный подоболочкой, копию содержимого файла?
- Почему удержание монопольной блокировки дескриптора файла
0
в одной оболочке не позволяет копии того же скрипта, работающей в другой оболочке, получить монопольную блокировку дескриптора файла0
? Не снаряды имеют свои собственные, отдельные копии стандартных файловых дескрипторов (0
,1
и2
, т.е. STDIN, STDOUT и STDERR)?
linux
shell-script
io-redirection
subshell
lock
sampablokuper
источник
источник
Ответы:
Вы можете использовать любой файл, если все копии скрипта используют один и тот же. Использование
$0
просто привязывает блокировку к самому сценарию: если вы копируете сценарий и модифицируете его для какого-либо другого использования, вам не нужно придумывать новое имя для файла блокировки. Это удобноЕсли скрипт вызывается с помощью символической ссылки, блокировка выполняется для самого файла, а не для ссылки.
(Конечно, если какой-то процесс запускает сценарий и присваивает ему в качестве нулевого аргумента выдуманное значение вместо фактического пути, то это нарушается. Но это редко делается.)
Вы уверены, что это из-за используемого файла, а не просто случайного отклонения? Как и в случае с конвейером, на самом деле нет способа быть уверенным в том, в каком порядке запускаются команды
cmd1 & cmd
. В основном это зависит от планировщика ОС. Я получаю случайные изменения в моей системе.Похоже, что это так, что сама оболочка содержит копию описания файла, содержащего блокировку, а не просто
flock
утилиту, удерживающую ее. Блокировка, созданная с помощьюflock(2)
, снимается, когда дескрипторы файлов, имеющие ее, закрываются.flock
имеет два режима: либо для блокировки на основе имени файла, и для запуска внешней команды (в этом случаеflock
содержится требуемый дескриптор открытого файла), либо для извлечения дескриптора файла извне, поэтому внешний процесс отвечает за удержание Это.Обратите внимание, что содержимое файла здесь не имеет значения, и копии не сделаны. Перенаправление на подоболочку не копирует никаких данных в себя, оно просто открывает дескриптор файла.
Да, но блокировка файла , а не дескриптор файла. Только один открытый экземпляр файла может одновременно удерживать блокировку.
Я думаю, что вы должны быть в состоянии сделать то же самое без вложенной оболочки, используя,
exec
чтобы открыть дескриптор файла блокировки:источник
{ }
вместо( )
будет также работать и избежать подоболочки.exec
.Блокировка файла прикрепляется к файлу через описание файла . На высоком уровне последовательность операций в одном экземпляре скрипта:
Удержание блокировки не позволяет запустить еще одну копию того же сценария, потому что это то, что делают блокировки. Пока в системе существует исключительная блокировка файла, невозможно создать второй экземпляр той же блокировки, даже с помощью другого описания файла.
Открытие файла создает описание файла . Это объект ядра, который не имеет прямой видимости в интерфейсах программирования. Вы обращаетесь к описанию файла косвенно через файловые дескрипторы, но обычно вы думаете о нем как о доступе к файлу (чтение или запись его содержимого или метаданных). Блокировка - это один из атрибутов, которые являются свойством описания файла, а не файла или дескриптора.
В начале, когда файл открывается, описание файла имеет один дескриптор файла, но можно создать больше дескрипторов либо путем создания другого дескриптора (
dup
семейства системных вызовов), либо путем разветвления подпроцесса (после чего родительский и ребенок имеет доступ к тому же описанию файла). Дескриптор файла может быть закрыт явно или когда процесс, в котором он находится, умирает. Когда последний дескриптор файла, прикрепленный к файлу, закрывается, описание файла закрывается.Вот как последовательность операций выше влияет на описание файла.
<$0
открывает файл сценария в подоболочке, создавая описание файла. На данный момент к описанию прикреплен единственный дескриптор файла: дескриптор номер 0 в подоболочке.flock
и ждет его выхода. Во время работы flock к описанию прикреплены два дескриптора: номер 0 в подоболочке и номер 0 в процессе стека. Когда flock берет блокировку, это устанавливает свойство описания файла. Если другое описание файла уже имеет блокировку файла, flock не может взять блокировку, так как это эксклюзивная блокировка.Причина, по которой сценарий использует перенаправление,
$0
заключается в том, что перенаправление является единственным способом открыть файл в оболочке, а сохранение активного перенаправления является единственным способом сохранить дескриптор файла открытым. Подоболочка никогда не читает со своего стандартного ввода, она просто должна оставаться открытой. На языке, который дает прямой доступ к открытию и закрытию вызова, вы можете использоватьНа самом деле вы можете получить ту же последовательность операций в оболочке, если вы делаете перенаправление с помощью
exec
встроенной функции.Сценарий может использовать другой файловый дескриптор, если он хочет сохранить доступ к исходному стандартному вводу.
или с ракушкой:
Блокировка не должна быть в файле сценария. Это может быть любой файл, который может быть открыт для чтения (поэтому он должен существовать, это должен быть тип файла, который можно прочитать, например обычный файл или именованный канал, но не каталог, и процесс сценария должен иметь разрешение на его чтение). Преимущество файла сценария в том, что он гарантированно присутствует и доступен для чтения (за исключением крайнего случая, когда он был удален извне между моментом вызова сценария и моментом, когда сценарий достигает
<$0
перенаправления).Пока это
flock
удается, а сценарий находится в файловой системе, где блокировки не содержат ошибок (некоторые сетевые файловые системы, такие как NFS, могут содержать ошибки), я не вижу, как использование другого файла блокировки может привести к состоянию гонки. Я подозреваю ошибку манипуляции с вашей стороны.источник
Файл, используемый для блокировки, не важен, скрипт использует,
$0
потому что это файл, который, как известно, существует.Порядок получения блокировок будет более или менее случайным, в зависимости от того, насколько быстро ваша машина сможет запустить две задачи.
Вы можете использовать любой файловый дескриптор, не обязательно 0. Блокировка удерживается для файла, открытого для файлового дескриптора, а не для самого дескриптора.
источник