Предположим, у меня есть процесс, который порождает ровно один дочерний процесс. Теперь, когда родительский процесс завершается по какой-либо причине (обычно или ненормально, из-за kill, ^ C, сбоя подтверждения или чего-то еще), я хочу, чтобы дочерний процесс умер Как это сделать правильно?
Некоторые похожие вопросы по stackoverflow:
- (спросили ранее) Как я могу заставить дочерний процесс завершать работу, если родительский процесс завершается?
- (спрашивается позже) Дочерние процессы, созданные с помощью fork (), автоматически уничтожаются при уничтожении родителя?
Несколько похожих вопросов о stackoverflow для Windows :
prctl()
в условиях гонки бесплатно. Кстати, ответ Максима неверен.man prctl
говорит: Установите для сигнала смерти родительского процесса вызывающего процесса значение arg2 (либо значение сигнала в диапазоне 1..maxsig, либо 0 для сброса). Это сигнал, который вызывающий процесс получит, когда его родитель умирает. Это значение очищается для потомка fork (2) и (начиная с Linux 2.4.36 / 2.6.23) при выполнении двоичного файла set-user-ID или set-group-ID.Я пытаюсь решить ту же проблему, и поскольку моя программа должна работать на OS X, решение только для Linux не сработало для меня.
Я пришел к тому же выводу, что и другие люди на этой странице - нет POSIX-совместимого способа уведомления ребенка о смерти родителя. Таким образом, я запутался в следующей лучшей вещи - проведении детского опроса.
Когда родительский процесс умирает (по любой причине), родительский процесс ребенка становится процессом 1. Если дочерний процесс просто периодически опрашивает, он может проверить, равен ли его родительский элемент 1. Если это так, дочерний процесс должен выйти.
Это не очень хорошо, но это работает, и это проще, чем решения опроса TCP-сокетов / файлов блокировки, предложенные в другом месте на этой странице.
источник
gettpid()
она не становится 1, а получаетpid
планировщик зоны (процессzsched
).Я достиг этого в прошлом, запустив «оригинальный» код в «дочернем» и «порожденный» код в «родительском» (то есть: вы изменили обычный смысл теста после
fork()
). Затем поместите SIGCHLD в «порожденный» код ...Может быть не возможно в вашем случае, но мило, когда это работает.
источник
Если вы не можете изменить дочерний процесс, вы можете попробовать что-то вроде следующего:
Это запускает дочерний процесс из процесса оболочки с включенным управлением заданиями. Дочерний процесс создается в фоновом режиме. Оболочка ожидает перевода строки (или EOF), а затем убивает дочернего элемента.
Когда родитель умирает - независимо от причины - он закроет свой конец трубы. Дочерняя оболочка получит EOF из чтения и продолжит убивать фоновый дочерний процесс.
источник
dup2
и ввода stdin, используяread -u
флаг для чтения из определенного дескриптора файла. Я также добавилsetpgid(0, 0)
в дочерний элемент, чтобы предотвратить его выход при нажатии ^ C в терминале.dup2()
вызова неверен. Если вы хотите использовать вpipes[0]
качестве стандартного ввода, вы должны написатьdup2(pipes[0], 0)
вместоdup2(0, pipes[0])
. Этоdup2(oldfd, newfd)
где вызов закрывает ранее открытый newfd.В Linux вы можете установить родительский сигнал смерти у ребенка, например:
Обратите внимание, что хранение идентификатора родительского процесса до разветвления и его тестирование в дочернем процессе
prctl()
устраняет условие гонки междуprctl()
выходом процесса, вызвавшего дочерний процесс.Также обратите внимание, что родительский сигнал смерти ребенка очищается у вновь созданных детей самостоятельно. Это не зависит от
execve()
.Этот тест можно упростить, если мы уверены, что системный процесс, отвечающий за усыновление всех сирот, имеет PID 1:
Однако полагаться на то, что системный процесс
init
имеет PID 1, нельзя переносить. POSIX.1-2008 определяет :Традиционно системный процесс, принимающий всех сирот, - это PID 1, т. Е. Init - предок всех процессов.
В современных системах, таких как Linux или FreeBSD , эту роль может выполнять другой процесс. Например, в Linux процесс может вызвать себя,
prctl(PR_SET_CHILD_SUBREAPER, 1)
чтобы установить себя как системный процесс, который наследует всех сирот любого из его потомков (см. Пример в Fedora 25).источник
init(8)
процессом ... единственное, что вы можете предположить, это то, что, когда родительский процесс умирает, это то, что его родительский идентификатор изменится. Это на самом деле происходит один раз в жизни процесса .... и когда умирает родитель процесса. Есть только одно главное исключение, это касаетсяinit(8)
детей, но вы защищены от этого, какinit(8)
никогдаexit(2)
(ядро в этом случае паникует)Ради полноты. В macOS вы можете использовать kqueue:
источник
NSTask
или posix spawn. СмотритеstartTask
функцию в моем коде здесь: github.com/neoneye/newton-commander-browse/blob/master/Classes/...Есть ли у дочернего процесса канал в / из родительского процесса? Если это так, вы получите SIGPIPE при записи или EOF при чтении - эти условия могут быть обнаружены.
источник
Вдохновленный другим ответом здесь, я придумал следующее решение для всех POSIX. Общая идея состоит в том, чтобы создать промежуточный процесс между родителем и потомком, который имеет одну цель: заметить, когда родитель умирает, и явно убить потомка.
Этот тип решения полезен, когда код в дочернем элементе не может быть изменен.
Есть два небольших предостережения с этим методом:
Кроме того, фактический код, который я использую, находится на Python. Вот для полноты картины:
источник
Я не верю, что можно гарантировать, что используются только стандартные вызовы POSIX. Как и в реальной жизни, когда ребенок рождается, у него появляется собственная жизнь.
это является возможным для родительского процесса , чтобы поймать наиболее возможное прекращение событий, и попытаться убить процесс ребенка в тот момент, но всегда есть некоторые , которые не могут быть перехвачены.
Например, ни один процесс не может поймать
SIGKILL
. Когда ядро обрабатывает этот сигнал, оно убивает указанный процесс без какого-либо уведомления этого процесса.Продлить аналогию - единственный другой стандартный способ сделать это - ребенок совершить самоубийство, когда обнаружит, что у него больше нет родителя.
Существует только Linux-способ сделать это
prctl(2)
- см. Другие ответы.источник
Как уже отмечали другие люди, полагаться на родительский pid, чтобы стать 1, когда родительский выход является непереносимым. Вместо ожидания определенного идентификатора родительского процесса, просто подождите, пока идентификатор изменится:
Добавьте микро-сон по желанию, если вы не хотите опрашивать на полной скорости.
Этот вариант кажется мне более простым, чем использование канала или использование сигналов.
источник
getpid()
делается в родительском до вызоваfork()
. Если родитель умирает до этого, ребенка не существует. Может случиться так, что ребенок некоторое время живет с родителем.Установите обработчик ловушек, чтобы поймать SIGINT, который убивает ваш дочерний процесс, если он еще жив, хотя другие авторы верны, что он не поймает SIGKILL.
Откройте файл .lock с монопольным доступом и попросите его открыть дочерний опрос - если открытие завершится успешно, дочерний процесс должен завершиться.
источник
Это решение сработало для меня:
Это был процесс рабочего типа, существование которого имело смысл только тогда, когда родитель был жив.
источник
Я думаю, что быстрый и грязный способ - создать канал между дочерним элементом и родительским элементом. Когда родитель выходит, дети получат SIGPIPE.
источник
На некоторых афишах уже упоминались трубы и
kqueue
. Фактически вы также можете создать пару подключенных доменных сокетов Unix с помощьюsocketpair()
вызова. Тип сокета должен бытьSOCK_STREAM
.Предположим, у вас есть два дескриптора файла сокета fd1, fd2. Теперь
fork()
для создания дочернего процесса, который будет наследовать FDS. В родительском вы закрываете fd2, а в дочернем вы закрываете fd1. Теперь каждый процесс можетpoll()
открыть оставшийся открытый файл для своегоPOLLIN
события. Пока каждая сторона явно не указываетclose()
свой fd в течение обычного времени жизни, вы можете быть совершенно уверены, чтоPOLLHUP
флаг должен указывать завершение другой (независимо от того, чист он или нет). После уведомления об этом событии ребенок может решить, что ему делать (например, умереть).Вы можете попробовать скомпилировать приведенный выше код для проверки концепции и запустить его в терминале, например
./a.out &
. У вас есть примерно 100 секунд, чтобы поэкспериментировать с уничтожением родительского PID различными сигналами, иначе он просто выйдет. В любом случае, вы должны увидеть сообщение «child: parent Hangable».По сравнению с методом, использующим
SIGPIPE
обработчик, этот метод не требует попыткиwrite()
вызова.Этот метод также симметричен , то есть процессы могут использовать один и тот же канал для мониторинга существования друг друга.
Это решение вызывает только функции POSIX. Я пробовал это в Linux и FreeBSD. Я думаю, что это должно работать на других Unix, но я действительно не проверял.
Смотрите также:
unix(7)
страниц человека Linux,unix(4)
для FreeBSD,poll(2)
,socketpair(2)
,socket(7)
на Linux.источник
В POSIX функции
exit()
,_exit()
и_Exit()
определены для:Таким образом, если вы организуете, чтобы родительский процесс был управляющим процессом для его группы процессов, дочерний процесс должен получить сигнал SIGHUP при выходе из родительского процесса. Я не совсем уверен, что происходит, когда родительский сбой, но я думаю, что это происходит. Конечно, для случаев без сбоев все должно работать нормально.
Обратите внимание , что вы , возможно , придется прочитать довольно много мелким шрифтом - в том числе в разделе Base Определения (Определения), а также информацию о системных служб для
exit()
иsetsid()
иsetpgrp()
- получить полную картину. (Я бы тоже!)источник
Если вы отправляете сигнал на pid 0, используя, например,
этот сигнал отправляется всей группе процессов, таким образом, эффективно убивая ребенка.
Вы можете легко проверить это с чем-то вроде:
Если вы затем нажмете ^ D, вы увидите текст
"Terminated"
как указание на то, что интерпретатор Python действительно был уничтожен, а не просто закрыт из-за закрытия стандартного ввода.источник
(echo -e "print(2+2)\n" & kill 0) | sh -c "python -"
радостно печатает 4 вместо уничтоженныхВ случае, если это имеет отношение к кому-либо еще, когда я порождаю экземпляры JVM в разветвленных дочерних процессах из C ++, единственный способ заставить экземпляры JVM правильно завершиться после завершения родительского процесса - это сделать следующее. Надеюсь, кто-то может оставить отзыв в комментариях, если это не лучший способ сделать это.
1)
prctl(PR_SET_PDEATHSIG, SIGHUP)
перед запуском Java-приложения вызовите разветвленный дочерний процессexecv
, и2) Добавьте хук отключения к приложению Java, которое опрашивает, пока его родительский PID не станет равным 1, затем выполните хард
Runtime.getRuntime().halt(0)
. Опрос выполняется путем запуска отдельной оболочки, которая запускаетps
команду (см .: Как мне найти мой PID в Java или JRuby в Linux? ).РЕДАКТИРОВАТЬ 130118:
Кажется, это не было надежным решением. Я все еще немного пытаюсь понять нюансы происходящего, но я все еще иногда получал бесхозные процессы JVM при запуске этих приложений в сеансах screen / SSH.
Вместо того, чтобы опрашивать PPID в приложении Java, я просто заставил хук выключения выполнить очистку с последующей полной остановкой, как описано выше. Затем я убедился, что вызывал
waitpid
в родительском приложении C ++ порожденный дочерний процесс, когда пришло время завершать все. Это кажется более надежным решением, так как дочерний процесс гарантирует его завершение, в то время как родитель использует существующие ссылки, чтобы убедиться, что его дочерние элементы завершаются. Сравните это с предыдущим решением, в котором родительский процесс завершался всякий раз, когда ему было удобно, и когда дети пытались выяснить, не осиротели ли они до завершения.источник
PID equals 1
Ждать не действует. Новый родитель может быть другим PID. Вы должны проверить, изменяется ли он от исходного родителя (getpid () перед fork ()) на нового родителя (getppid () в потомке, который не равен getpid () при вызове перед fork ()).Другой способ сделать это, специфичный для Linux, - создать родителя в новом пространстве имен PID. Тогда это будет PID 1 в этом пространстве имен, и когда оно выйдет из него, все его дети будут немедленно убиты
SIGKILL
.К сожалению, для создания нового пространства имен PID вам необходимо иметь
CAP_SYS_ADMIN
. Но этот метод очень эффективен и не требует никаких реальных изменений для родителей или дочерних элементов после первоначального запуска родителя.См. Clone (2) , pid_namespaces (7) и unshare (2) .
источник
Если родитель умирает, PPID сирот изменится на 1 - вам нужно только проверить свой PPID. В некотором смысле, это опрос, упомянутый выше. вот кусок оболочки для этого:
источник
Я нашел 2 решения, оба не идеальные.
1. Убить всех детей с помощью kill (-pid) при получении сигнала SIGTERM.
Очевидно, что это решение не может обрабатывать «kill -9», но оно работает для большинства случаев и очень просто, потому что ему не нужно запоминать все дочерние процессы.
Таким же образом вы можете установить обработчик 'exit', как описано выше, если вы вызываете process.exit где-нибудь. Примечание: Ctrl + C и внезапный сбой были автоматически обработаны ОС для уничтожения группы процессов, поэтому здесь больше нет.
2.Use chjj / pty.js , чтобы мицелий ваш процесс с управляющим терминалом прилагается.
Когда вы в любом случае уничтожаете текущий процесс, даже уничтожая -9, все дочерние процессы также будут автоматически уничтожаться (ОС?). Я предполагаю, что, поскольку текущий процесс удерживает другую сторону терминала, поэтому, если текущий процесс умирает, дочерний процесс получит SIGPIPE, поэтому он умрет.
источник
Мне удалось создать переносимое решение без опроса с 3 процессами, злоупотребив терминальным управлением и сеансами. Это умственная мастурбация, но работает.
Хитрость заключается в следующем:
Туда:
Недостатки:
источник
Несмотря на то, что прошло 7 лет, я только что столкнулся с этой проблемой, так как я запускаю приложение SpringBoot, которому нужно запустить webpack-dev-server во время разработки и убить его, когда бэкэнд-процесс останавливается.
Я пытаюсь использовать
Runtime.getRuntime().addShutdownHook
но это работает на Windows 10, но не на Windows 7.Я изменил его, чтобы использовать специальный поток, который ожидает завершения процесса или
InterruptedException
который, кажется, работает правильно в обеих версиях Windows.источник
Исторически в UNIX v7 система процессов обнаружила потерю процессов, проверив идентификатор родительского процесса. Как я уже говорил, исторически
init(8)
системный процесс - это особый процесс только по одной причине: он не может умереть. Он не может умереть, потому что алгоритм ядра, чтобы иметь дело с назначением нового идентификатора родительского процесса, зависит от этого факта. когда процесс выполняет свой процесс, то процесс становится сиротой перед системным вызовом.exit(2)
вызов (посредством системного вызова процесса или внешней задачи как отправка ему сигнала или тому подобного), ядро переназначает всем дочерним элементам этого процесса идентификатор процесса init в качестве идентификатора их родительского процесса. Это приводит к самому легкому тестированию и наиболее портативному способу узнать, не стал ли процесс потерянным. Просто проверьте результатgetppid(2)
системного вызова, и если это идентификатор процессаinit(2)
При таком подходе возникают две проблемы, которые могут привести к проблемам:
init
Во- первых, у нас есть возможность изменить процесс на любой пользовательский процесс. Как мы можем гарантировать, что процесс init всегда будет родительским для всех потерянных процессов? Что ж, вexit
коде системного вызова есть явная проверка, чтобы увидеть, является ли процесс, выполняющий вызов, процессом init (процесс с pid, равным 1), и если это так, ядро паникует (оно больше не должно поддерживать иерархия процессов), поэтому процессу init не разрешается делатьexit(2)
вызов.1
таковым, но это не гарантируется подходом POSIX, который утверждает (как показано в другом ответе), что для этой цели зарезервирован только идентификатор процесса системы. Практически ни одна реализация posix не делает этого, и вы можете предположить, что в исходных системах, производных от Unix, в1
качестве ответа наgetppid(2)
системный вызов достаточно предположить, что процесс потерян. Другой способ проверить это сделатьgetppid(2)
сразу после разветвления и сравнить это значение с результатом нового вызова. Это просто не работает во всех случаях, так как оба вызова не являются атомарными, и родительский процесс может умереть послеfork(2)
и до первогоgetppid(2)
системного вызова. Выход процессаparent id only changes once, when its parent does an
(2)call, so this should be enough to check if the
getppid (2)result changed between calls to see that parent process has exit. This test is not valid for the actual children of the init process, because they are always children of
init (8) `, но вы можете также предположить, что эти процессы не имеют родителя (кроме случаев, когда вы заменяете в системе процесс init)источник
Я передал родительский pid с использованием среды дочернему элементу, а затем периодически проверял, существует ли / proc / $ ppid от дочернего элемента.
источник