Примечание Простой однострочник с kill 0, $pid
в конце прокомментирован.
Нам нужно обнаружить завершение внешней программы, которая не была запущена этим скриптом. Вопрос касается использования waitpid
. Чтобы скопировать мой ранний комментарий:
Тебе нельзя. Вы можете ждать только дочерний процесс. См. perldoc wait
(или waitpid
, то же самое), первое предложение.
wait
и waitpid
ждут сигналов, доставленных сценарию относительно судьбы его потомка (детей). Скрипту незачем получать такие сигналы о процессах, которые он не запускал.
Мы знаем идентификатор процесса и его имя. Его PID можно использовать для опроса, работает ли он. Использование pid
само по себе не совсем надежно, так как между нашими проверками процесс может завершиться и случайному новому присвоить тот же самый pid
. Мы можем использовать название программы, чтобы усилить это.
В системе Linux информацию о процессе можно получить, используя (множество) ps
опций. Любой из них возвращает полный вызов программы
ps --no-headers -o cmd PID
ps --no-headers -p PID -o cmd
Возвращаемая строка может начинаться с пути к интерпретатору (например, для Perl-скрипта), за которым следует полное имя программы. Версия ps -p PID -o comm=
возвращает только имя программы, но я обнаружил, что это слово может разбиваться на дефис (если он есть), что приводит к неполному имени. Это может потребовать настройки на некоторых системах, обратитесь к man ps
. Если нет процесса с заданным PID, мы ничего не получаем.
Затем мы можем проверить PID и, если он найден, проверить, соответствует ли имя этого PID программе. Имя программы известно, и мы могли бы просто жестко запрограммировать его. Тем не менее, он по-прежнему получается сценарием при запуске с помощью приведенной выше команды ps
, чтобы избежать двусмысленностей. (Тогда оно также находится в том же формате для последующего сравнения.) Это само по себе проверяется на известное имя, поскольку нет гарантии, что PID во время выполнения сценария действительно соответствует ожидаемой программе.
use warnings;
use strict;
# For testing. Retrieve your PID as appropriate for real use
my $ext_pid = $ARGV[0] || $$;
my $cmd_get_name = "ps --no-headers -o cmd $ext_pid";
# For testing. Replace 'sleep' by your program name for real use
my $known_prog_name = 'sleep';
# Get the name of the program with PID
my $prog_name = qx($cmd_get_name);
# Test against the known name, exit if there is a mismatch
if ($prog_name !~ $known_prog_name) {
warn "Mismatch between:\n$prog_name\n$known_prog_name -- $!";
exit;
}
my $name;
while ( $name = qx($cmd_get_name) and $name =~ /$prog_name/ )
{
print "Sleeping 1 sec ... \n";
sleep 1;
}
# regex above may need slight adjustment, depending on format of ps return
Вывод команды, полученный с помощью qx()
выше (оператор обратной кавычки), содержит новую строку. Если это окажется проблемой в том, что делает сценарий, он может быть chomp
-ed, что потребует небольшой корректировки. Оставшаяся лазейка в том, что эта та самая программа могла завершиться и перезапуститься между проверками и с тем же PID.
Это будет проверено запуском в оболочке
sleep 30 &
script.pl `ps aux | egrep '[s]leep'`
egrep
это grep -E
. Вывод из `ps ...`
содержит несколько слов. Они передаются в качестве аргументов командной строки нашему сценарию, который использует первый в качестве PID. Если есть проблема с этим, сначала запустите фильтрацию ps
, а затем вручную введите PID в качестве входного аргумента скрипта. sleep
из 30 секунд, указанных выше, дают достаточно времени, чтобы сделать все это в командной строке.
Код можно упростить, сопоставив $name
с жестко заданным $prog_name
, если имя программы достаточно уникально и не изменится. Жестко запрограммированное имя используется выше, но для проверки и выдает предупреждение в случае несоответствия. (Если мы полагаемся на него только в жестком коде, мы не можем выдавать предупреждения, если он не соответствует, поскольку тогда это является частью работы кода.)
Если процесс принадлежит тому же пользователю, что и скрипт, можно использовать kill 0, $pid
, т.к.
while ( kill 0, $ext_pid ) { sleep 1 }
Тогда вам придется либо сделать еще один вызов, чтобы проверить имя, либо довольствоваться (небольшой) возможностью ошибки в том, какой фактический процесс представляет $pid
.
Для большей части этого можно использовать модуль Proc::ProcessTable
.
person
zdim
schedule
17.05.2016
$pid
это то, что вы думаете? Каково возвращаемое значениеwaitpid
? Он укажет, какой процесс завершился. - person Schwern   schedule 17.05.2016wait
в дочернем процессе. См.perldoc wait
(илиwaitpid
, это одно и то же), первое предложение. Если вы хотите знать, когда завершается внешняя программа, вам нужно делать совершенно другие вещи. - person zdim   schedule 17.05.2016