Pull to refresh

Comments 20

Сохранять пид в файле, и проверять активен ли этот пид, нет?

code
#!/bin/bash
pidf=file.pid
pid=$(cat $pidf)
[[ -e /proc/${pid:-0} ]] && { echo fail; exit 1; }

echo  $$ > $pidf
sleep $1

Думаю, вы сами найдете в этом скрипте все race conditions, где оно заглючит. Блокировка - чертовски хрупкая штука в целом, когда нет нужного готового и оттестированного примитива в среде.

Можно и без файла обойтись:

code
#!/bin/bash
ps -ef | grep -v $$ | grep $0 && exit 1

sleep $1
echo FIN

Если вы параллельно запустили редактирование vim /path/to/script ваш подход не сработает. Если случилось так, что у вашего скрипта пид 1111, а уже работает процесс с пидом 11111, то тоже не сработает.

Технически, PID не уникален и может быть выдан другому процесс в промежутках между проверками, а вы потом будете вечно ждать его завершения, если это что-то долгоживущее окажется.

Самый простой способ – создать в /tmp файл с любым именем и дать команду rename -o mytemp.file my-script.lock my-script.lock для переименования его в my-script.lock. Нулевой код возврата из переименования – работаем, ненулевой – отваливаемся.

А если mytemp.file не создастся?

Плохо дело, если не создаются файлы в /tmp

Что делать, если скрипт завершился аварийно и не удалил my-script.lock?

Надо скрипт вызывать из другого скрипта, который удаляет my-script.lock. А так вообще этот вопрос неразрешим на сто процентов, только с большой вероятностью. Может и задача на семафоре подвиснуть, и пид повторно выделиться.

Но ведь при удаление my-script.lock происходит race condition?

Race condition не происходит, просто по жизни не определено, какой в точности момент считать окончанием работы скрипта. Когда скрипт уже вышел на терминальную ветку и начал завершаться, уже можно запускать вторую копию или ещё нельзя?

А как вы поступаете в своих скриптах, использующих такой подход?

Надо по смыслу операции смотреть. Обычно периодически запускаемые скрипты, которые нужно обсемафоривать, ничего не теряют от того, что какой-то один раз не запустятся.

Например, если это суточный бэкап в полночь, и так получилось, что он сам по себе продолжается сутки, то нам не очень важно, завершился он в 00:00:01 или в 23:59:59 – в любом случае, пропустив следующий запуск в 00:00:00, мы фактически ничего не теряем.

Не претендую на сколь-нибудь полное понимание вопроса, но приведу свою версию запрета выполнения нескольких экземпляров скрипта:

Здесь race condition конечно же: этот скрипт стартует одновременно дважды, оба процесса входят в ветку else, и вот вам две копии запущенные. Причем кажется, что оно должно редко происходить, но если вдруг на 10-ядерной машине окажется ненадолго load average 50 (баг где-нибудь или что-то еще), то вероятность многократно возрастает.

Не надо недооценивать и велосипедить с блокировками, это очень опасно. В блокировках есть точно работающее решение, не вероятностное, а строгое, которое работает при любой нагрузке. Его и надо использовать.

Это какой-то вид садизма, вместо кода выкладывать скриншот из notepad++?

В баше лучше однозначно flock, но его нет в MacOS! (Как это вообще возможно в 2022 году, что flock нет в MacOS и нет в Node, для меня полная загадка.)

Вариант автора с pid-ами… ну спорно: несмотря на все тесты, есть ли четкое доказательство, что оно прям всегда работает? а с условием ротации пидов? а если Load Average искусственно загнать под сотню?

Зато железобетонный flock есть в… Perl. И perl есть во всех OS, причем один и тот же, и запускается быстро. Поэтому если надо уж прям совсем кросс-платформенное скриптовое решение, то я использую perl + exec в нем (процесс умирает - лок сам освобождается, и за счет exec от perl-а не остается и следа в pstree).

Ротация пидов действительно может вызвать проблемы. Возможно для учета не только пида, но и имени процесса, можно использовать ps -eo pid,command, но я не знаю насколько портабельно это решение.

Sign up to leave a comment.

Articles