LINUX.ORG.RU

bash запускает отдельный процесс для под-оболочек в пайпах

 , ,


0

5

Минимальный скрипт test_pipe_pid:

#!/bin/bash
echo "started PID=$$"
trap 'echo killed PID=$$; exit 1' INT
yes | while read line; do
	trap 'echo killed inner PID=$$; exit 1' INT
	jobs -p
	sleep 1
done

Запускаем и пытаемся убить по основному PID-у:

~$ ./test_pipe_pid &
[1] 2320
started PID=2320
~$ pstree -p 2320
test_pipe_pid(2320)─┬─test_pipe_pid(2326)───sleep(2563)
                    └─yes(2324)
~$ kill -INT 2320
# А он живой:
~$ pstree -p 2320
test_pipe_pid(2320)─┬─test_pipe_pid(2326)───sleep(3021)
                    └─yes(2324)
~$ kill -INT 2326
# И только теперь умер:
~$ killed inner PID=2320
killed PID=2320

[1]+  Выход 1            ./test_pipe_pid

Откуда берётся дополнительный процесс? Его не видно в jobs -p, и он перехватывает обработку сигналов. Как тогда, зная родительский pid, прервать весь скрипт?

При запуске в foreground и посыле Ctrl-C напрямую скрипт убивается целиком, с потрохами. А в чём тогда принципиальная разница?

Как это гуглить? Гугл не знает, что такое «bash pass signal to subshell» в любых комбинациях. В мане есть только заметка «Each command in a pipeline is executed as a separate process (i.e., in a subshell).» Нашёл рецепт делать trap 'kill 0' INT, но ничего не изменилось, сабшелл продолжает жить, пока его не убьёшь прямо.

Зачем это нужно? Есть некий процесс, который запускает разные команды, в т.ч. и такой скрипт. По определённому запросу извне процесс должен закрывать все запущенные команды. Остальные команды закрываются по SIGINT как ожидается, только такой Маклауд с пайпами остаётся.

★★★

можно убивать по PGID'у:

 kill -- -2320 
2320 - PGID, тот же, что и родительский PID. В синтаксисе kill передаётся со знаком минус.

urquan ()
Ответ на: комментарий от urquan

Как костыль вполне подойдёт, спасибо.

E ★★★ ()

Откуда берётся дополнительный процесс?

Отсюда:| while

Можешь развернуть задом наперёд и делать read в основном баше:

#!/bin/bash
echo "started PID=$$"
trap 'echo killed PID=$$; exit 1' INT
while read line; do
        trap 'echo killed inner PID=$$; exit 1' INT
        jobs -p
        sleep 1
done < <(yes)
legolegs ★★★★★ ()
Ответ на: комментарий от legolegs

Это исправит только единичный случай, да и пайп там довольно нетривиальный, если переписывать, то на нормальном ЯП

У меня проблема не с этим конкретным скриптом как таковым, а вообще с поведением баша в отношении подпроцессов.

E ★★★ ()
Ответ на: комментарий от E

Тебе всё верно сказали, bash создаёт субпроцессы согласно логике обработки оператора |. Это же не интерпретатор полноценного ЯП, это интерпретатор команд, и он работает на уровне абстракций, которые даёт операционная система.

Так что либо писать аккуратно, с пониманием, где именно будут вызовы fork(), либо переписывать на более мощный ЯП.

wandrien ()
Ответ на: комментарий от wandrien

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

E ★★★ ()
Ответ на: комментарий от wandrien

Интересное академическое исследование, но, к сожалению, не имеет ни отношения к заданному вопросу, ни практического применения.

E ★★★ ()
Для того чтобы оставить комментарий войдите или зарегистрируйтесь.