Ни один из ваших сценариев не завершает sleep
, и вы делаете его более запутанным, отправляя USR1 с помощью pkill
. Поскольку фоновое задание является форком переднего плана, они имеют одно и то же имя (trap-test.sh
); так что pkill
соответствует и сигнализирует обоим. Это в неопределенном порядке убивает фоновый процесс (оставляя sleep
живым, объяснено ниже) и запускает ловушку на переднем плане, отсюда и условие гонки.
Кроме того, в приведенных вами примерах фоновое задание всегда просто sleep x
, но в вашем скрипте это sleep 10 && echo 'doing some work'
; который требует, чтобы раздвоенный подоболочек ожидал завершения sleep
и выполнял условно echo
. Сравните эти два:
$ sleep 10 &
[1] 9401
$ pstree 9401
sleep
$
$ sleep 10 && echo foo &
[2] 9410
$ pstree 9410
bash───sleep
Итак, давайте начнем с нуля и воспроизведем основную проблему в терминале.
$ set +m
$ sleep 100 && echo 'doing some work' &
[1] 9923
$ pstree -pg $$
bash(9871,9871)─┬─bash(9923,9871)───sleep(9924,9871)
└─pstree(9927,9871)
$ kill $!
$ pgrep sleep
9924
$ pkill -e sleep
sleep killed (pid 9924)
Я отключил элементы управления заданиями, чтобы частично имитировать поведение неинтерактивной оболочки.
Уничтожение фонового задания не убило sleep
, мне нужно было завершить его вручную. Это произошло из-за того, что сигнал, отправленный процессу, не передается автоматически дочерним элементам его цели; то есть sleep
вообще не получил сигнал TERM.
Чтобы уничтожить sleep
, а также подоболочку, мне нужно поместить фоновое задание в в отдельную группу процессов - который требует включения элементов управления заданиями, в противном случае все задания помещаются в группу процессов основной оболочки, как показано в выводе pstree
выше, и отправляют ему сигнал TERM, как показано ниже.
$ set -m
$ sleep 100 && echo 'doing some work' &
[1] 10058
$ pstree -pg $$
bash(9871,9871)─┬─bash(10058,</code><b>10058</b><code>)───sleep(10059,</code><b>10058</b><code>)
└─pstree(10067,10067)
$ kill -- </code><b>-$!</b><code>
$
[1]+ Terminated sleep 100 && echo 'doing some work'
$ pgrep sleep
$
С некоторой доработкой и адаптацией этой концепции ваш сценарий будет выглядеть так:
#!/bin/bash -
set -m
usr1_handler() {
kill -- -$!
echo 'doing some work'
}
do_something() {
trap '' USR1
sleep 10 && echo 'doing some work'
}
trap usr1_handler USR1 EXIT
echo "my PID is $$"
while true; do
do_something &
wait
done
Это выведет my PID is xxx
(где xxx
- PID процесса переднего плана) и начнет цикл. Отправка сигнала USR1 на xxx
(т. Е. kill -USR1 xxx
) вызовет прерывание и вызовет завершение фонового процесса и его дочерних элементов. Таким образом, wait
вернется и l oop продолжится.
Если вы используете pkill
, вместо этого он все равно будет работать, так как фоновый процесс игнорирует USR1.
Для получения дополнительной информации см .: