Стоит задача - есть некий скрипт, который производит определенные действия с определенной периодичностью. Вызов скрипта в определенном случае может заблокироваться (допустим, ожиданием ввода-вывода). При этом следом может запуститься вторая копия скрипта (интервал не велик), третья и далее. Что в итоге приведет к тому, что система после разблокировки рухнет. Допустим, наш скрипт зовут: /usr/bin/ispmanager_fix_fastcgi и написан он на Перле.
Для начала алгоритм - узнаем имя текущего скрипта (ну не захардкоживать же его в самом деле), грепаем выдачу ps aux (текущие запущенные в системе процессы) на предмет наличия указанного. Если находим, то просто завершаемся - значит предыдущий запуск скрипта еще продолжает работать.
Во-первых, тут нам понадобится спец-переменная $0, которая содержит имя исполняемой программы. В моем случае это будет "/usr/bin/ispmanager_fix_fastcgi". То есть, код будет такой:
ps aux | grep "/usr/bin/ispmanager_fix_fastcgi"Далее нам надо будет исключить из выдачи ps aux скрипт, из которго сделан запрос проверки. Тут нам поможет встроенная переменная $$, содержащая pid текущего процесса, который в пределах системы, разумеется, уникален. То есть, у нас получается нечто вида:
ps aux | grep "/usr/bin/ispmanager_fix_fastcgi" | grep -v "pid"
Также в выдачу ps aux обязательно попадет сам grep, поэтому его также нужно исключить:
ps aux | grep "/usr/bin/ispmanager_fix_fastcgi" | grep -v "pid" | grep -v grep
Итого получается следующий Perl код:
#!/usr/bin/perl
use strict;
use warnings;
unless ( system("ps aux | grep $0 | grep -v grep | grep -v $$ | grep -v '/bin/sh -c' > /dev/null") ) {
print "Duplicate!\n";
exit 0; # another copy already running
}
# тут код параллельного запуска которого мы и собираемся избегать
Исключение /bin/sh -c добавлено для Debian cron, где каждая cron задача запускается как два процесса.
Конечно, решение не подойдет для форкащихся процессов, которые имеют одинаковые имена, не подойдет оно и для скриптов запускающихся друг за другом, но если интервал между запусками велик и от него не зависят жизни людей (тут уже ключевые слова звучат, например, как "flock"), то юзать вполне можно :)
Как вариант "красивого решения", могу предложить модуль: Proc::ProcessTable, который на Debian ставится вот так:
apt-get install -y libproc-processtable-perl
Решение для Python
Логику рассуждений повторять не буду, приведу лишь код.
import os pid = os.getpid() Nil, scriptname = os.path.split(sys.argv[0]) result = os.system("ps aux | grep '" + scriptname + "' | grep -v grep | grep -v '" + str(pid) + "' > /dev/null" ) if result == 0: print "Another copy of script already running" sys.exit(0)
Мутексы уже не модно чтоли ? )) как то странно дергать ps, фильтровать его вывод для таких целей...
ReplyDeleteХз, из Перла это геморно помойму. Тут решить надо было быстро и просто :)
ReplyDelete