URL: https://www.opennet.ru/cgi-bin/openforum/vsluhboard.cgi
Форум: vsluhforumID9
Нить номер: 10311
[ Назад ]

Исходное сообщение
"Завершение дочерних процессов 2 уровня"

Отправлено Аноним , 13-Май-20 01:56 
Подскажите, как лучше его организовать? Ситуация следующая: дети запускаются фоном, после чего они рождают ещё детей. Мне нужно всех их вырезать, потому что, если их не прибить собственноручно, они остаются жить навсегда. Добился нужного через pkill -g $$ в конце (повесил на выход), но возникают определённые подозрения (в частности в связи с тем, что при самоубийстве в баше не появляется приглашение, видимо фишка баша для провисших дескрипторов) или же вообще ломается любой ввод (он не отображается). Я так понял, это вообще проблема, в линуксе нельзя просто взять и узнать, кого мы родили. Мне только pstree -p $bgjobpid показывает, что там кто-то где-то висит во внучках (он обходит всех на предмет родителей?), разбирать выхлоп pstree чёт видится извратом. Вообще, в интернете обещали и что pkill -P $$ будет достаточно, да только дети детей то остаются!

Суть дела: мне нужно убить всех детей вместе с их детьми средствами, доступными родителю, в идеале получить перечень пидов и их перебить. Я могу получить пид мейна, скажем, через ppid/pgrp/tpgid, или даже того, кто запускал (баш) из sid, но я не могу получить перечень порождённых процессов и желательно перебить их всех по цепочке? Если не останавливать фоновый процесс через прямое убийство (что я делал изначально, поскольку я прекрасно вижу его в мейне), он вместе со всеми порождёнными (уже не видимыми мне) им переходит под инит при завершении. Если останавливать, порождённые им процессы остаются висеть (под инитом). Пробовал уже по-всякому, и kill, и pkill, и всё остальное, но внучки остаются недосягаемыми.

Из информации, полученной со стаковерфлоу, я сделал вывод, что единственный выход тут, это использовать неймспейс (и грохать его). Так ли это? Как же живут пользователи других систем?

И я смотрю unshare с --kill-child=sigkill как-то странно реагирует на ^C -- я так понимаю ему прилетает kill и sigint не доходит до приложения, в следствии чего я не могу обработать этот кейс в коде -- всё слишком быстро умирает. Или даже не так, но умирает всё равно раньше, чем я успеваю почистить файлы в перехватчике int. А с другими сигналами kill-child (term,int), всё умирает, но приглашение не появляется. Т.е. неймспейсы тут не подходят.

Это конечно хорошо, что мой код работает, но плохо, что я его не понимаю. Чем мне грозит pkill -g $$ на практике? Видимых проблем я не вижу, всё в полном порядке, вот только провисшие дескрипторы меня пугают.


Содержание

Сообщения в этом обсуждении
"Завершение дочерних процессов"
Отправлено Аноним , 13-Май-20 03:02 
Что-то не то с порядком исполнения, или же временем? Если на exit повешено несколько действий, прилетает sigterm. Если там один pkill -g $$, всё нормально завершается. повесил новую ловушку на выход в ловушке на выход. Что-то странное, я всё чаще начинаю замечать, что не понимаю, почему мой баш работает и как он это делает.

"Завершение дочерних процессов"
Отправлено Аноним , 13-Май-20 07:19 
Да тебе бы сценарии к триллерам писать, а не вот это вот всё) ))

"Завершение дочерних процессов"
Отправлено eRIC , 13-Май-20 13:32 
предлагаю название темы поменять, потому-что по таким словам как: "убийство внучек", "суицид" ресурс OpenNet не забанили

"Завершение дочерних процессов"
Отправлено Сейд , 05-Июн-20 20:37 
Поменяли, но внизу на главной странице название почему-то осталось: https://i.imgur.com/yD5x4Dz.png

"Завершение дочерних процессов 2 уровня"
Отправлено Аноним , 13-Май-20 14:39 
> Ситуация следующая: дети запускаются фоном, после чего они рождают ещё детей.

Породив внуков, дети завершаются или остаются жить? Тут нужно либо обучить детей передавать прилетающие SIGTERM'ы и SIGINT'ы внукам (см. встроенные в bash команды trap и jobs), либо как-то передавать список внуков родителю.

> что единственный выход тут, это использовать неймспейс (и грохать его).

В Linux ещё control groups, и они, как мне представляется, более подходят к данному случаю.


"Завершение дочерних процессов 2 уровня"
Отправлено Аноним , 13-Май-20 17:22 
Они живут пока мамка жива, если мейн сворачивается по любой из причин внучки должны быть завершены сразу после детей (чтобы детям от них данные не попали и не было гонки). С внучками ещё проблема в том, что они даже не баш и не могут реагировать на изменения предка.

Я попробовал навесить трапов на фоновый жоб, так я всё равно не могу узнать кого мы родили и там! И начинается вообще какая-то дичь, которую я не могу контролировать (сейчас хотя бы порядок срабатывания обработчиков не нарушается, всё разруливается в мейне имеющем все необходимые данные). У меня нет доступа в детях к внучкам, потому что дети ждут пока внуки завершатся или умрут (это даже не важно, что из двух) и это событие тригеррит детей, которые после этого обрабатывают полученные данные и взаимодействуют с мейном (через файл на диске, угу).

Я так понимаю для работы с cgroups мне понадобится модификация системы и суидные рутовые бинарники? Потому что неймспейсы могут быть пользовательскими (чем весь софт вроде браузеров пользуется и суидные песочницы это вроде как уже не модно), а cgroups от пользователя можно как-то управлять исключительно через systemd,


"Завершение дочерних процессов 2 уровня"
Отправлено Аноним , 13-Май-20 20:38 
> У меня нет доступа в детях к внучкам, потому что дети ждут пока внуки завершатся или умрут

Дети — bash? Внуков можно запускать в фоновом режиме (команда &), тогда доступ к ним остаётся. Также можно ждать завершения фоновых процессов встроенной командой wait.

Bash, кстати, при получении SIGTERM'а автоматически передаёт его всем фоновым потомкам.

> Я попробовал навесить трапов на фоновый жоб, так я всё равно не могу узнать кого мы родили и там!

jobs выдаёт список фоновых процессов, не?

> Я так понимаю для работы с cgroups мне понадобится модификация системы и суидные рутовые бинарники?

Достаточно systemd-run или пользовательской сессии systemd. Возможно, эту всю скриптоту удобнее писать не на bash'е, а на юнитах systemd.


"Завершение дочерних процессов 2 уровня"
Отправлено Аноним , 13-Май-20 21:19 
Вот пример кода:

EXBGFILE=$(mktemp -p /dev/shm/ --suffix=wdfile)

sendvalue() {
  echo "${1}">${EXBGFILE}
}
readvalue() {
  echo "$(<${EXBGFILE})"
}
sendvalue 0

bgloop() {
  while sleep 9 & wait; do
    echo "exec"
    var=$(readvalue)
    echo "${FUNCNAME} read:${var}"
  done
}

bgloop &
bgjob=$!
trap "kill '$bgjob'; rm -v -- '${EXBGFILE}'" EXIT

echo `jobs -l`
echo `jobs -p`
sendvalue 2
sleep 10
sendvalue 5
sleep 10
sendvalue 9
sleep 10
sendvalue 11
echo done

Если я посылаю ^C скрипту, sleep 9 переходит под инит до завершения (kill останавливает только фоновый жоб и sleep никак не остановить). Сейчас я вешаю такую конструкцию на завершение мейна, чтобы убить себя, и при этом прилетало и внучке. У меня есть ощущение, что лучше обойтись без самоубийства (остановки группы процессов, к которой принадлежат все порождённые процессы включая мейн).

trap "kill '$bgjob'; rm -v -- ${EXBGFILE}; trap 'pkill -g $$' EXIT;" EXIT;

При этом kill '$bgjob' (как и jobs -p в мейне) вообще не надёжная тема, процесса может и не оказаться, если мы дёргаем sigint, и тогда килл жалуется, мол, но сач процесс. Однако, pkill -g $$ всегда завершает всех без эксцессов (при таком использовании -- если в trap exit что-то постороннее, прилетает terminated).


"Завершение дочерних процессов 2 уровня"
Отправлено Аноним , 13-Май-20 21:24 
Ой я имел в виду у меня конечно sigint отдельно, всё равно после завершения остаются висеть под мейном. Я запустил этот пример, и у меня слипы плодятся несколько раз, но последний переходит под инит и висит до завершения и вот он-то мне и нужен исчезнувшим сразу.

"Завершение дочерних процессов 2 уровня"
Отправлено Аноним , 13-Май-20 21:25 
Нет, со слипами всё в порядке. А вот по завершении висеть под инитом последний слип дочки (он же внучка) остаётся.

"Завершение дочерних процессов 2 уровня"
Отправлено Аноним , 13-Май-20 21:55 
Нужно сделать ещё один trap в bgloop():

#!/usr/bin/env bash
trap 'kill %' EXIT

bgloop() {
    trap 'kill %' EXIT
    while sleep 10 & wait; do
        :
    done
}
bgloop &
wait


Так при прибитии родителя прибиваются также все дети.

"Завершение дочерних процессов 2 уровня"
Отправлено Аноним , 13-Май-20 22:25 
Похоже, то что нужно, спасибо. Только всё ещё не понятно, почему $bgjob и jobs -p сообщают о процессе, который не существует с точки зрения kill при sigint. Он исчезает из-за sigint? И как мне его останавливать тогда, если для мейна его уже нет из-за sigint? Он может и быть, а может и не быть. Неприятненько, куда лучше, когда код без плавающих багов.

"Завершение дочерних процессов 2 уровня"
Отправлено Аноним , 14-Май-20 12:11 
> почему $bgjob и jobs -p сообщают о процессе, который не существует с точки зрения kill при sigint.
> Он может и быть, а может и не быть..

Сейчас проверил, своим потомкам, если это не внешние программы, bash пересылает сигналы завершения сам автоматически, т.е. в коде #10 первый trap излишен, а $bgjob из #7, видимо, был прибит до вызова kill.

> Если отправлять pkill -g $$ в мейне, то bgloop получает только int (не term) и до обработчика exit дело не доходит.

trap ... EXIT срабатывает и на INT (который также прилетает по ctrl+c), и на TERM.

> смерть не мгновенная?

Естественно: используемые сигналы можно перехватить и обработать. Нельзя обработать KILL, SEGV и некоторые другие.


"Завершение дочерних процессов 2 уровня"
Отправлено Аноним , 14-Май-20 17:14 
> Сейчас проверил, своим потомкам, если это не внешние программы, bash пересылает сигналы
> завершения сам автоматически, т.е. в коде #10 первый trap излишен, а
> $bgjob из #7, видимо, был прибит до вызова kill.

1 trap там не излишен, он вообще не работает -- bgloop переходит под инит и остаётся висеть. Он должен быть заменён на

bgloop &
trap "kill $!" EXIT

Тогда работает, но только если bgloop остановлен kill (sigterm) в мейне. Если bgloop прерывается sigint (что происходит не каждый раз)

>> Если отправлять pkill -g $$ в мейне, то bgloop получает только int (не term) и до обработчика exit дело не доходит.
> trap ... EXIT срабатывает и на INT (который также прилетает по ctrl+c),
> и на TERM.

срабатывает, только exit отдельно стрелял после int при нажатии ^C, теперь только на int. Я уже сталкивался с тем, что простые примеры ведут себя не так, как чуть более сложные (из-за разного времени реакции системы надо полагать)

>> смерть не мгновенная?
> Естественно: используемые сигналы можно перехватить и обработать. Нельзя обработать KILL,
> SEGV и некоторые другие.

Дело не в обработке, я опасаюсь, что возникнет гонка из-за задержки между отправкой сигнала и реакцией на него. В примере из #10 bgloop дети переходят под мейн и продолжают работать бесконечно . И мне не надо bgloop& wait, мне надо чтобы он работал параллельно, в #10 мы в мейне ждём завершения фонового процесса, которое не случится пока мы не прервём. Мейн должен работать и передавать данные в bgloop, я ведь не зря привёл такой пример в #7.


"Завершение дочерних процессов 2 уровня"
Отправлено Аноним , 14-Май-20 20:25 
> И мне не надо bgloop& wait, мне надо чтобы он работал параллельно, в #10 мы в мейне ждём завершения фонового процесса, которое не случится пока мы не прервём.

В #10 главный скрипт завершается до завершения bgloop'а, в этом проблема. Мэйн должен либо что-то делать, либо ждать завершения детей, но не завершаться сам. Можно запихнуть wait в конец, после кормёжки детей данными, это спасёт от состояния гонки.


"Завершение дочерних процессов 2 уровня"
Отправлено Аноним , 15-Май-20 20:05 
Поэтому я и пытаюсь их прибить до выхода, но я даже не знаю кого. Думал на тему того, чтобы передавать пиды внучек через диск. но что-то и так уже слишком много файлов генерирую. Попробовал повесить wait на sigexit в мейне, что-то эксперименты не увенчались успехом и bgloop замечательно продолжает жить отдельно после нормального завершения мейна. Но sigint останавливает, это да.

"Завершение дочерних процессов 2 уровня"
Отправлено Licha Morada , 15-Май-20 21:19 
> Поэтому я и пытаюсь их прибить до выхода, но я даже не
> знаю кого.

Можно поддержиавать в актуальном состоянии список всех детей.
После каждого порождения, добавлять его PID в список:
CHILDERN_PIDS="$CHILDERN_PIDS $!"
https://stackoverflow.com/questions/2618403/how-to-kill-all-...

Можно узнавать список "всех детей" непосредственно перед прибиванием.
CHILDERN_PIDS=$(ps -o pid= --ppid $$)

Потом по CHILDERN_PIDS можно итерировать в простом цикле for.
Делать отдельно в каждом поколении в отношении непосредственных детей, либо рекурсивно из "дедушки".
Тут хорошо описанно:
https://unix.stackexchange.com/questions/124127/kill-all-des...

Должно работать если:
1. Дети есть строго дети, в терминах иерархии процессов, а не демонизированные сироты.
2. Дети успевают сделать что-то осмысленное после того как их прибивают и до, собственно, умирания.
Рекурсия из дедушки, в отличии от отдельной итерации в каждом поколении, смягчает ограничение 2.


> Думал на тему того, чтобы передавать пиды внучек через
> диск. но что-то и так уже слишком много файлов генерирую.

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



"Завершение дочерних процессов 2 уровня"
Отправлено Аноним , 19-Май-20 02:11 
> sigexit

Нет такого сигнала. EXIT — это обобщение bash'а для обработки сигналов, по которым он должен завершаться, а также нормального его завершения.

Но я не пойму, зачем вешать wait в trap, когда его можно разместить в конце программы.


"Завершение дочерних процессов 2 уровня"
Отправлено Аноним , 13-Май-20 22:57 
Заметил что при таком использовании работает корректно, только если в bgloop прилетают оба сигнала (sigint и sigkill), если kill $bgpid не отправлять из мейна (выходе), то фоновый процесс останется висеть и вместе с внучками перейдёт под инит. Или он может остановится sigint, и тогда из мейна его уже не убить на выходе. Если отправлять pkill -g $$ в мейне, то bgloop получает только int (не term) и до обработчика exit дело не доходит. Кроме того, 1 раз я успел заметить, что внучка перед смертью переходит под инит (в принципе это не плохо), это значит смерть не мгновенная?

"Завершение дочерних процессов 2 уровня"
Отправлено user user , 20-Май-20 15:24 
чисто из любопытства, какой сценарий привел победе fork на pthread_create?