Уничтожение дочернего процесса в сценарии оболочки:
Много раз нам нужно убивать дочерние процессы, которые по какой-то причине зависают или блокируются. например. Проблема с FTP-соединением.
Есть два подхода,
1) Создать отдельного нового родителя для каждого дочернего элемента, который будет отслеживать и уничтожать дочерний процесс после истечения времени ожидания.
Создайте test.sh следующим образом,
#!/bin/bash
declare -a CMDs=("AAA" "BBB" "CCC" "DDD")
for CMD in ${CMDs[*]}; do
(sleep 10 & PID=$!; echo "Started $CMD => $PID"; sleep 5; echo "Killing $CMD => $PID"; kill $PID; echo "$CMD Completed.") &
done
exit;
и наблюдайте за процессами, которые имеют имя «test» в другом терминале, используя следующую команду.
watch -n1 'ps x -o "%p %r %c" | grep "test" '
Вышеописанный скрипт создаст 4 новых дочерних процесса и их родителей. Каждый дочерний процесс будет работать в течение 10 секунд. Но по истечении 5 секунд соответствующие родительские процессы убьют этих детей.
Таким образом, ребенок не сможет завершить выполнение (10 секунд).
Поиграйте в эти моменты (переключатели 10 и 5), чтобы увидеть другое поведение. В этом случае дочерний процесс завершит выполнение за 5 секунд до истечения времени ожидания 10 секунд.
2) Пусть текущий родительский монитор отслеживает и уничтожает дочерний процесс по истечении времени ожидания. Это не создаст отдельного родителя для мониторинга каждого ребенка. Также вы можете правильно управлять всеми дочерними процессами в пределах одного и того же родителя.
Создайте test.sh следующим образом,
#!/bin/bash
declare -A CPIDs;
declare -a CMDs=("AAA" "BBB" "CCC" "DDD")
CMD_TIME=15;
for CMD in ${CMDs[*]}; do
(echo "Started..$CMD"; sleep $CMD_TIME; echo "$CMD Done";) &
CPIDs[$!]="$RN";
sleep 1;
done
GPID=$(ps -o pgid= $$);
CNT_TIME_OUT=10;
CNT=0;
while (true); do
declare -A TMP_CPIDs;
for PID in "${!CPIDs[@]}"; do
echo "Checking "${CPIDs[$PID]}"=>"$PID;
if ps -p $PID > /dev/null ; then
echo "-->"${CPIDs[$PID]}"=>"$PID" is running..";
TMP_CPIDs[$PID]=${CPIDs[$PID]};
else
echo "-->"${CPIDs[$PID]}"=>"$PID" is completed.";
fi
done
if [ ${#TMP_CPIDs[@]} == 0 ]; then
echo "All commands completed.";
break;
else
unset CPIDs;
declare -A CPIDs;
for PID in "${!TMP_CPIDs[@]}"; do
CPIDs[$PID]=${TMP_CPIDs[$PID]};
done
unset TMP_CPIDs;
if [ $CNT -gt $CNT_TIME_OUT ]; then
echo ${CPIDs[@]}"PIDs not reponding. Timeout reached $CNT sec. killing all childern with GPID $GPID..";
kill -- -$GPID;
fi
fi
CNT=$((CNT+1));
echo "waiting since $b secs..";
sleep 1;
done
exit;
и наблюдайте за процессами, которые имеют имя «test» в другом терминале, с помощью следующей команды.
watch -n1 'ps x -o "%p %r %c" | grep "test" '
Вышеуказанный скрипт создаст 4 новых дочерних процесса. Мы храним pids всех дочерних процессов и зацикливаем их, чтобы проверить, закончили ли они свое выполнение или все еще работают.
Дочерний процесс будет выполняться до времени CMD_TIME. Но если тайм-аут CNT_TIME_OUT достигнут, все дочерние процессы будут убиты родительским процессом.
Вы можете переключать время и играть со скриптом, чтобы увидеть поведение.
Недостатком этого подхода является использование идентификатора группы для уничтожения всего дочернего дерева. Но сам родительский процесс принадлежит к той же группе, поэтому он также будет уничтожен.
Вам может потребоваться назначить другой идентификатор группы родительскому процессу, если вы не хотите, чтобы родительский процесс был убит.
Более подробную информацию можно найти здесь,
Уничтожение дочернего процесса в сценарии оболочки