Использование pid-файлов для предотвращения повторного запуска скрипта

Использование модулей File::Pid, Pid::File::Flock и File::Flock::Tiny. Блокировки файлов, работа с pid-файлами. Работа только одной копии скрипта в один момент времени. Защита от повторного запуска одного и того же скрипта, до того, как первый экземпляр завершит свою работу.

Допустим, что есть perl-скрипт, который забирает данные с удаленных серверов. Или разносит данные из одного источника в таблицы БД. Скрипт работает достаточно долго, от получаса и более - зависит от объема данных, скорости ответа удаленных серверов и т.п. Запускается по cron, раз в час.

Во избежание проблем с дублированием данных, взаимными блокировками в БД и т.п., необходимо чтобы скрипт всегда был запущен только в одном экземпляре.

Для этого можно использовать pid-файл. При запуске, скрипт проверяет наличие pid-файла. Если файл доступен, то блокирует его и сохраняет в файле свой PID, и только после этого приступает к выполнению своей основной работы. Если файл существует, но доступ к нему заблокирован - скрипт сразу же завершает свою работу. Если pid-файл не существует, то скрипт создает его, блокирует, сохраняет свой pid, и приступает к основной работе.

Для того, чтобы не изобретать велосипед, можно найти на cpan модули для работы с pid-файлами. В простых приложениях их будет вполне достаточно.

 

File::Flock::Tiny

File::Flock::Tiny - очень удобный модуль для работы с pid-файлами.

Установка проходит без проблем.

Пример использования, pid2.pl:

Сохраняем скрипт и запускаем его. После этого будет создан файл script.pid с ID процесса внутри.

Смотрим список процессов, убедиться, что скрипт работает:

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

 

Методы File::Flock::Tiny

File::Flock::Tiny->lock($file)

Эксклюзивная блокировка файла. $file - может быть как именем файла, так и файловым дескриптором. Файл блокируется до того момента, пока процесс не выйдет из границ функции, в которой блокировка была установлена. Либо, можно вызвать метод разблокировки объекта. Если указанный файл не существовал - он будет создан.

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

 

File::Flock::Tiny->trylock($file)

Делает попытку заблокировать файл, если не получилось - возвращает undef.

 

File::Flock::Tiny->write_pid($file)

Делает попытку заблокировать указанный файл и сохранить в него ID текущего процесса. Если попытка удалась - возвращает ссылку на заблокированный объект, если нет - вернет undef. После завершения процесса файл не удаляется, но блокировка будет снята. При повторном выполнении файл будет вновь заблокирован, ID процесса в файле - обновлен.

 

$lock->write_pid

Удаляет все данные из заблокированного файла, если они были, и сохраняет в него ID текущего процесса.

 

$lock->release

Снимает блокировку и закрывает файл.

 

$lock->close

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

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

 

Pid::File::Flock

Пример использования, pid4.pl:

Создает или открывает уже существующий pid-файл, устанавливает блокировку, сохраняет в него ID процесса. После завершения работы блокировка снимается. Файл не удаляется.

При попытке запустить второй экземпляр скрипта, пока еще не завершил работу первый и с pid-файла не была снята блокировка, выдает ошибку:

 

Методы Pid::File::Flock

new( $path, %options )

$path - необязательный аргумент, который применяется, если параметры 'dir','name' and 'ext' по каким-либо причинам не будут использованы.

Параметры:

  • dir - директория, в которой будет создан pid-файл. Если не указано, то будет вызван File::Spec::tmpdir - который создаст файл в каком-нибудь каталоге /temp . Лучше указывать.
  • name - имя pid-файла.
  • ext - расширение для pid-файла. По умолчанию будет использоваться '.pid'.
  • debug => 1 - включает режим отладки. Начнет выдавать дополнительную информацию через STDERR.
  • quiet => 1 - включает "тихий" режим. Не выдает предупреждений об устаревших pid-файлах.

 

abandon

Указание не пытаться удалить файл в процессе завершения работы. Если abandon не использовать, pid-файл будет удален после завершения работы скрипта.

 

File::Pid

Попробовала использовать File::Pid для блокировки запуска скрипта. Не получилось.

Пример, pid.pl:

Сразу же возникли проблемы:

  • если файл с pid еще не существует (а откуда ему взяться, при первом запуске скрипта) - выводится ошибка.
  • если файл с pid создан, но сам pid в нем не записан - ошибка
  • если файл создан, и pid в нем указан, то pid текущего процесса в нем не сохраняется. Старый и уже не существующий pid продолжает сохраняться.

    Содержимое script.pid:

    Реальный процесс:

До проверки, действительно ли можно предотвратить повторный запуск скрипта с помощью
File::Pid не дошло. Не вижу смысла. Возможно, данный модуль стал не актуальным с течением времени, написан с ошибками (что более вероятно), и имеет проблемы с использованием на разных операционных системах.

Вспомнила, что File::Pid использовался в одной из систем, которую я встречала года 3 назад. Запуск скриптов там всегда был проблемой. Либо не запускались вообще, либо запускались в нескольких экземплярах. Теперь понятно - почему. Не рекомендую.

 

Полезные ссылки

search.cpan.org: File::Flock::Tiny

search.cpan.org: File::Pid

search.cpan.org: Pid::File::Flock

Использование pid-файлов для предотвращения повторного запуска скрипта: 2 комментария

  1. Savenkova Natalya

    Спасибо, интересно.

    Если процесс падает где-нибудь в середине, блокировка снимается?

    У меня везде есть подключение к memcached или redis, я там ставлю признак блокировки с expire. Если процесс упал и не удалил за собой lock, тогда он умрет сам по себе через какое-то время.

    1. Natalie Автор записи

      Зависит от того, какой модуль используется для проверки. Те, которые я приводила в пример, проверяют наличие блокировки и самого процесса в начале работы. Если процесса уже нет - то "блокировка" не действительна. В самом деле, там не используется блокировка на уровне операционной системы, когда я зашла и подредактировала pid-файл во время работы процесса, с помощью mc - мне система и слова не сказала :) Так что, если предполагается чей-то злонамеренный доступ к pid-файлам, надо перед внедрением такой системы, хорошенько ее проверить.

Комментарии запрещены.