Как избежать состояния гонки при использовании файла блокировки, чтобы избежать одновременного запуска двух экземпляров скрипта?

Типичный подход, позволяющий избежать одновременного запуска двух экземпляров одного и того же скрипта, выглядит следующим образом:

[ -f ".lock" ] && exit 1
touch .lock
# do something
rm .lock

Есть ли лучший способ заблокировать файлы из сценария оболочки, избегая состояния гонки? Должны ли вместо этого использоваться каталоги?


person n-alexander    schedule 28.11.2008    source источник
comment
Вопросы следует отмечать [решено] выбором ответа, а не редактированием заголовка и добавлением [решено].   -  person Barry Kelly    schedule 28.11.2008
comment
проблема в том, что я не могу отметить свой ответ как принятый   -  person n-alexander    schedule 12.12.2008
comment
Честно говоря, не думаю, что ваш ответ лучший :)   -  person Barry Kelly    schedule 04.02.2011


Ответы (5)


Да, в примере сценария действительно есть состояние гонки. Вы можете использовать опцию noclobber в bash, чтобы получить сбой в случае гонки, когда другой скрипт пробирается между тестом -f и touch.

Ниже приводится пример фрагмента кода (на основе этой статьи), который иллюстрирует механизм:

if (set -o noclobber; echo "$$" > "$lockfile") 2> /dev/null; 
then
   # This will cause the lock-file to be deleted in case of a
   # premature exit.
   trap 'rm -f "$lockfile"; exit $?' INT TERM EXIT

   # Critical Section: Here you'd place the code/commands you want
   # to be protected (i.e., not run in multiple processes at once).

   rm -f "$lockfile"
   trap - INT TERM EXIT
else
   echo "Failed to acquire lock-file: $lockfile." 
   echo "Held by process $(cat $lockfile)."
fi
person Barry Kelly    schedule 28.11.2008
comment
Если кому-то еще интересно, что делает $$, он возвращает PID (идентификатор процесса) запущенного скрипта. - person Chris W.; 15.07.2019

Попробуйте команду flock:

exec 200>"$LOCK_FILE"
flock -e -n 200 || exit 1

Он выйдет, если файл блокировки заблокирован. Это атомарно и будет работать с последней версией NFS.

Я сделал тест. Я создал файл счетчика с 0 в нем и выполнил в цикле на двух серверах одновременно 500 раз:

#!/bin/bash

exec 200>/nfs/mount/testlock
flock -e 200

NO=`cat /nfs/mount/counter`
echo "$NO"
let NO=NO+1
echo "$NO" > /nfs/mount/counter

Один узел боролся с другим за замок. Когда оба прогона завершились, содержимое файла было 1000. Я пробовал несколько раз, и он всегда работает!

Примечание. Клиент NFS - это RHEL 5.2, а используемый сервер - NetApp.

person jpastuszek    schedule 15.10.2009
comment
Достаточно хорошее покрытие для flock: в моей выборке он используется на Cygwin и Linux, но не на Solaris или Mac. - person Barry Kelly; 04.02.2011
comment
Не могли бы вы подробнее объяснить синтаксис? Меня особенно интересует exec 200>"$LOCK_FILE". Я собираюсь понять это с помощью справочных страниц, но ваш ответ был бы намного лучше, если бы он объяснил, что делают эти строки. - person Dr. Jan-Philip Gehrcke; 01.08.2013
comment
Руководство по Bash 3.2, раздел 3.6: Перенаправления с использованием файловых дескрипторов больше 9 следует использовать с осторожностью, поскольку они могут конфликтовать с файловыми дескрипторами, которые оболочка использует внутри. Интенсивное использование FD 200 (например, 500 раз подряд из нескольких процессов) может вызвать проблемы, не так ли? - person system PAUSE; 04.01.2014

Заблокируйте свой скрипт (от параллельного запуска)

http://wiki.bash-hackers.org/howto/mutex

К вашему сведению.

person Hank    schedule 06.03.2011

Создание каталога является атомарным и не работает, если он уже существует, если вы не используете -p, поэтому

 mkdir $lockName || exit 1

работает.

... но вместо этого используйте flock.

person Paul Hodges    schedule 10.08.2020

похоже, я нашел более простое решение: man lockfile

person n-alexander    schedule 28.11.2008
comment
Обратите внимание, что файл блокировки не переносится - он может быть недоступен; это часть procmail (AFAIK). - person Barry Kelly; 28.11.2008
comment
Оболочка более портативна, чем procmail, поскольку у вас больше шансов использовать bash, чем procmail. У меня здесь есть bash для Solaris, Linux, Mac и Windows. lockfile нет ни в одном из них. - person Barry Kelly; 04.02.2011
comment
Я написал bash-скрипт с использованием noclobber, который имитирует поведение файла блокировки Procmail. Он должен быть портативным, и вы можете найти его здесь: codng. ru / 2011/05 / file-locks-in-bash.html - person juancn; 18.05.2011