Как мне поместить уже запущенный процесс в nohup?
Вопрос
У меня есть процесс, который уже давно запущен, и я не хочу его завершать.
Как мне поместить его в nohup (то есть, как мне заставить его продолжать работать, даже если я закрою терминал?)
Решение
Использование управления заданиями в bash для отправки процесса в фон:
<Ол>bg
чтобы запустить его в фоновом режиме. disown -h [job-spec]
где [job-spec] - номер задания (например, %1
для первого запущенного задания; узнайте свой номер с помощью команды jobs
), чтобы задание не было убито при закрытии терминала . Другие советы
Предположим, по какой-то причине Ctrl + Z также не работает, перейдите в другой терминал, найдите идентификатор процесса (используя ps
) и выполните:
kill -SIGSTOP PID
kill -SIGCONT PID
SIGSTOP
приостановит процесс и SIGCONT
возобновит процесс в фоновом режиме. Теперь закрытие обоих терминалов не остановит ваш процесс. Р>
Команда для отделения работающего задания от оболочки (= делает его nohup) - disown
и базовая команда оболочки.
С bash-manpage (man bash):
disown [-ar] [-h] [jobspec ...]
Без параметров каждая спецификация заданий удаляется из таблицы активных заданий. Если задана опция -h, каждая спецификация заданий не удаляется из таблицы, но помечается так, что SIGHUP не отправляется на задание, если оболочка получает SIGHUP. Если нет заданий присутствует, и ни опция -a, ни опция -r не указаны, используется текущее задание. Если задание не указано, опция -a означает удалить или пометить все вакансии; опция -r без аргумента jobspec ограничивает выполнение операций заданиями. Возврат значение равно 0, если в задании не указано допустимое задание.
Это означает, что простой
disown -a
удалит все задания из таблицы заданий и сделает их нету
Это хорошие ответы выше, я просто хотел добавить уточнение:
Ты не можешь disown
pid или процесс, вы disown
работа, и это важное различие.
Задание - это нечто, представляющее собой понятие процесса, который привязан к оболочке, поэтому вы должны перевести задание в фоновый режим (не приостанавливать его), а затем отказаться от него.
Проблема:
% jobs
[1] running java
[2] suspended vi
% disown %1
Видишь http://www.quantprinciple.com/invest/index.php/docs/tipsandtricks/unix/jobcontrol/ для более подробного обсуждения управления заданиями в Unix.
К сожалению disown
относится только к bash и доступен не во всех оболочках.
Некоторые разновидности Unix (например, AIX и Solaris) имеют параметр самой команды nohup
, который можно применить к запущенному процессу:
nohup -p pid
Ответ Node действительно великолепен, но он оставил открытым вопрос о том, как перенаправить stdout и stderr.Я нашел решение по Unix и Linux, но она также не является полной.Я хотел бы объединить эти два решения.Вот оно:
Для моего теста я создал небольшой скрипт bash под названием loop.sh, который печатает pid самого себя с минутным переходом в бесконечный цикл.
$./loop.sh
Теперь каким-то образом получите PID этого процесса.Обычно ps -C loop.sh
достаточно хорош, но в моем случае он напечатан.
Теперь мы можем переключиться на другой терминал (или нажать ^Z и в том же терминале).Сейчас gdb
должен быть присоединен к этому процессу.
$ gdb -p <PID>
Это останавливает скрипт (если он запущен).Его состояние можно проверить с помощью ps -f <PID>
, где STAT
поле равно 'T+' (или в случае ^Z 'T'), что означает (man ps(1))
T Stopped, either by a job control signal or because it is being traced
+ is in the foreground process group
(gdb) call close(1)
$1 = 0
Close(1) возвращает ноль в случае успеха.
(gdb) call open("loop.out", 01102, 0600)
$6 = 1
Open(1) возвращает новый файловый дескриптор в случае успеха.
Это открытие равнозначно open(path, O_TRUNC|O_CREAT|O_RDWR, S_IRUSR|S_IWUSR)
.Вместо того, чтобы O_RDWR
O_WRONLY
можно было бы применить, но /usr/sbin/lsof
говорит "u" для всех обработчиков файлов std * (FD
столбец), который является O_RDWR
.
Я проверил значения в заголовочном файле /usr/include/bits/fcntl.h.
Выходной файл может быть открыт с помощью O_APPEND
, как nohup
подошел бы, но это не предлагается man open(2)
, из-за возможных проблем с NFS.
Если мы получим -1 в качестве возвращаемого значения, то call perror("")
выводит сообщение об ошибке.Если нам нужен errno, используйте p errno
команда gdb.
Теперь мы можем проверить только что перенаправленный файл. /usr/sbin/lsof -p <PID>
С принтами:
loop.sh <PID> truey 1u REG 0,26 0 15008411 /home/truey/loop.out
Если мы хотим, мы можем перенаправить stderr в другой файл, если мы хотим использовать call close(2)
и call open(...)
снова используя другое имя файла.
Теперь прилагаемый bash
его нужно освободить, и мы сможем уволиться gdb
:
(gdb) detach
Detaching from program: /bin/bash, process <PID>
(gdb) q
Если скрипт был остановлен gdb
с другого терминала он продолжает работать.Мы можем снова переключиться на терминал loop.sh.Теперь он ничего не выводит на экран, а запускается и записывает в файл.Мы должны отодвинуть это на задний план.Так что нажимайте ^Z
.
^Z
[1]+ Stopped ./loop.sh
(Сейчас мы находимся в том же состоянии, как если бы ^Z
был нажат в самом начале.)
Теперь мы можем проверить состояние задания:
$ ps -f 24522
UID PID PPID C STIME TTY STAT TIME CMD
<UID> <PID><PPID> 0 11:16 pts/36 S 0:00 /bin/bash ./loop.sh
$ jobs
[1]+ Stopped ./loop.sh
Таким образом, процесс должен быть запущен в фоновом режиме и отсоединен от терминала.Число в jobs
выходные данные команды в квадратных скобках идентифицируют задание внутри bash
.Мы можем использовать в следующих встроенных bash
команды, ставящие знак "%" перед номером задания :
$ bg %1
[1]+ ./loop.sh &
$ disown -h %1
$ ps -f <PID>
UID PID PPID C STIME TTY STAT TIME CMD
<UID> <PID><PPID> 0 11:16 pts/36 S 0:00 /bin/bash ./loop.sh
И теперь мы можем выйти из игры calling bash.Процесс продолжает выполняться в фоновом режиме.Если мы завершим работу, его PPID станет равным 1 (процесс init (1)), а управляющий терминал станет неизвестным.
$ ps -f <PID>
UID PID PPID C STIME TTY STAT TIME CMD
<UID> <PID> 1 0 11:16 ? S 0:00 /bin/bash ./loop.sh
$ /usr/bin/lsof -p <PID>
...
loop.sh <PID> truey 0u CHR 136,36 38 /dev/pts/36 (deleted)
loop.sh <PID> truey 1u REG 0,26 1127 15008411 /home/truey/loop.out
loop.sh <PID> truey 2u CHR 136,36 38 /dev/pts/36 (deleted)
Комментарий
Содержимое gdb можно автоматизировать, создав файл (например,loop.gdb), содержащий команды и выполняющий gdb -q -x loop.gdb -p <PID>
.Мой loop.gdb выглядит примерно так:
call close(1)
call open("loop.out", 01102, 0600)
# call close(2)
# call open("loop.err", 01102, 0600)
detach
quit
Или вместо этого можно использовать следующий однострочник:
gdb -q -ex 'call close(1)' -ex 'call open("loop.out", 01102, 0600)' -ex detach -ex quit -p <PID>
Я надеюсь, что это достаточно полное описание решения.
В моей системе AIX я пробовал
nohup -p processid>
Это сработало хорошо. Он продолжал запускать мой процесс даже после закрытия окон терминала. В качестве оболочки по умолчанию используется ksh, поэтому команды bg
и disown
не работают.
bg
- это переведет задание в фоновый режим и вернется в рабочий процесс disown -a
- это обрезает все вложения с заданием (так что вы можете закрыть терминал, и он все равно будет работать) Эти простые шаги позволят вам закрыть терминал, продолжая процесс.
Он не наденется nohup
(исходя из моего понимания вашего вопроса, он вам здесь не нужен).
Это сработало для меня в Ubuntu Linux, когда я был в tcshell.
<Ол>Ctrl Z , чтобы приостановить его
bg
для запуска в фоновом режиме
jobs
чтобы получить номер своей работы
nohup %n
где n - номер задания