Как я могу запустить новый процесс, который НЕ является дочерним элементом исходного процесса?

(OSX 10.7) Используемое приложение позволяет назначать сценарии для вызова, когда определенные действия происходят в приложении. Я назначил bash script и он называется, проблема в том, что мне нужно выполнить несколько команд, подождать 30 секунд, а затем выполнить еще несколько команд. Если у меня есть bash script сделать "sleep 30", все приложение замерзает в течение 30 секунд, пока мой script закончит.

Я попытался перевести 30-секундное ожидание (и второй набор команд) в отдельный script и вызвать "./secondScript &". но приложение все еще сидит там в течение 30 секунд, ничего не делая. Я предполагаю, что приложение ожидает завершения script и всех дочерних процессов.

Я пробовал эти варианты для вызова второго script из основного script, все они имеют одинаковую проблему:

  • nohup./secondScript &
  • ((./secondScript и) и)
  • (./secondScript и)
  • nohup script -q/dev/null secondScript &

У меня нет возможности изменить приложение и сообщить ему, чтобы он запустил мой script и не дождался его завершения.

Как запустить процесс (я бы предпочел, чтобы процесс был на языке сценариев), так что новый процесс не является дочерним элементом текущего процесса?

Спасибо, Крис

p.s. Я попробовал команду "отменить", и это тоже не помогло. Мой главный script выглядит следующим образом:

[initial commands]
echo Launching second script
./secondScript &
echo Looking for jobs
jobs
echo Sleeping for 1 second
sleep 1
echo Calling disown
disown
echo Looking again for jobs
jobs
echo Main script complete

и что я получаю для вывода:

Launching second script
Looking for jobs
[1]+ Running ./secondScript &
Sleeping for 1 second
Calling disown
Looking again for jobs
Main script complete

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

p.p.s

Если в верхней части основного script я выполняю "ps", единственное, что он возвращает, это идентификатор процесса интерактивного сеанса bash, который я открывал в отдельном окне терминала.

Значение $SHELL:/bin/bash

Если я выполняю "ps -p $$", он правильно сообщает мне

PID   TTY TIME    CMD
26884 ??  0:00.00 mainScript

Если я выполняю "lsof -p $$", он дает мне все виды результатов (я не вставлял все столбцы здесь, считая их не соответствующими):

FD   TYPE   NAME
cwd  DIR    /private/tmp/blahblahblah
txt  REG    /bin/bash
txt  REG    /usr/lib/dyld
txt  REG    /private/var/db/dyld/dyld_shared_cache_x86_64
0    PIPE   
1    PIPE   -> 0xffff8041ea2d10
2    PIPE   -> 0xffff 8017d21cb
3r   DIR    /private/tmp/blahblah
4r   REG    /Volumes/DATA/blahblah
255r REG    /Volumes/DATA/blahblah

Ответ 1

Типичным способом сделать это в Unix является двойная вилка. В bash вы можете сделать это с помощью

( sleep 30 & )

(..) создает дочерний процесс, а & создает процесс внука. Когда дочерний процесс умирает, процесс внука наследуется init.


Если это не работает, ваше приложение не ждет дочерних процессов.

Другие вещи, которые он может ожидать, включают сеанс и открытые файлы блокировки:

Чтобы создать новый сеанс, Linux имеет setsid. В OS X вы можете сделать это через script, который, кстати, также создает новый сеанс:

# Linux:
setsid sleep 30

# OS X:
nohup script -q -c 'sleep 30' /dev/null &

Чтобы найти список наследуемых дескрипторов файлов, вы можете использовать lsof -p yourpid, который выведет что-то вроде:

sleep   22479 user    0u   CHR 136,32      0t0       35 /dev/pts/32
sleep   22479 user    1u   CHR 136,32      0t0       35 /dev/pts/32
sleep   22479 user    2u   CHR 136,32      0t0       35 /dev/pts/32
sleep   22479 user    5w   REG  252,0        0  1048806 /tmp/lockfile

В этом случае, помимо стандартных FD 0, 1 и 2, у вас также есть fd 5 open с файлом блокировки, который может ждать родитель.

Чтобы закрыть fd 5, вы можете использовать exec 5>&-. Если вы считаете, что файл блокировки может быть сам stdin/stdout/stderr, вы можете использовать nohup для перенаправления их на что-то еще.

Ответ 2

Я думаю, это зависит от того, как ваш родительский процесс пытается определить, завершился ли ваш дочерний процесс. В моем случае (мой родительский процесс был gnu make), мне удалось закрыть stdout и stderr (немного в зависимости от ответа этого другого парня) следующим образом:

sleep 30 >&- 2>&- &

Вы также можете закрыть stdin

sleep 30 <&- >&- 2>&- &

или дополнительно отключить дочерний процесс (не для Mac)

sleep 30 <&- >&- 2>&- & disown

В настоящее время тестируется только в bash на kubuntu 14.04 и Mac OSX.

Ответ 3

Другой способ - отказаться от дочернего

#!/bin/bash

yourprocess &

disown

Насколько я понимаю, приложение заменяет обычную оболочку bash, потому что она все еще ждет завершения процесса, даже если init должен был позаботиться об этом дочернем процессе. Может быть, "приложение" перехватывает обработку сирот, которая обычно выполняется с помощью init.

В этом случае только параллельный процесс с некоторым IPC может предложить решение (см. мой другой ответ)

Ответ 4

Если все остальное не выполнено:

  • Создайте именованный канал

  • запустите "медленный" script независимый из "приложения", убедитесь, что он выполняет его задачу в бесконечном цикле, начиная с чтения из канала. Когда он попытается прочитать, он станет блокированным для чтения.

  • из приложения, запустите свой другой script. Когда ему нужно вызвать "медленный" script, просто напишите некоторые данные в трубе. Медленный script начнет независимо, чтобы ваш script не дождался завершения "медленного" script.

Итак, чтобы ответить на вопрос:
bash - как я могу запустить новый процесс, который НЕ является дочерним элементом исходного процесса?

Просто: не запускайте его, но пусть независимый объект запускает его во время загрузки... например init или "на лету" с помощью команды at или batch

Ответ 5

Здесь у меня есть оболочка

└─bash(13882)

Где я начинаю такой процесс:

$ (urxvt -e ssh somehost&)

Я получаю дерево процессов (этот вывод отключен от pstree -p):

├─urxvt(14181)───ssh(14182)

где процесс подпадает под pid 1 (systemd в моем случае).

Однако, если бы я сделал это (обратите внимание, где находится &):

$ (urxvt -e ssh somehost)&

тогда процесс будет дочерним элементом оболочки:

└─bash(13882)───urxvt(14181)───ssh(14182)

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

В последнем случае дерево процессов восстанавливается ниже pid 1, когда оболочка выходит, поэтому она заканчивается тем же, что и в первом примере.

├─urxvt(14181)───ssh(14182)

В любом случае результатом является дерево процессов, которое переживает оболочку. Единственное отличие - это начальное родительское поведение этого дерева процессов.

Для справки вы также можете использовать

  • nohup urxvt -e ssh somehost &
  • urxvt -e ssh somehost & disown $!

Оба дают то же дерево процессов, что и второй пример выше.

└─bash(13882)───urxvt(14181)───ssh(14182)

Когда оболочка завершается, дерево процессов, как и прежде, повторно к pid 1.

nohup дополнительно перенаправляет стандартный вывод процесса в файл nohup.out, поэтому, если это полезная черта, это может быть более полезным выбором.

В противном случае, с первой формой выше, вы сразу получите полностью отсоединенное дерево процессов.