bash получение идентификатора фонового процесса дает родительский pid - PullRequest
2 голосов
/ 31 марта 2020

Создание сценария bash с помощью этой команды:

cat <<"END"> z
#! /bin/bash

sleep 20 && exit 1 &
ret=$!

ps $ret | grep $ret
END

и последующее его выполнение дает:

7230 pts/39   S+     0:00 /bin/bash ./z

Я ожидал увидеть sleep 20 ..., который является дочерним процессом , Если я удаляю && exit 1, он возвращает дочерний процесс.

В чем причина? Как я могу получить идентификатор дочернего процесса в приведенном выше утверждении?

Ответы [ 3 ]

1 голос
/ 01 апреля 2020

В чем причина?

Причина в том, что какая-то сущность должна сделать &&. Это не может быть sleep, потому что sleep только спит, а после завершения sleep (то есть больше нет sleep для принятия какого-либо решения), некоторому «объекту» необходимо сравнить состояние выхода sleep и решить, а затем выполнить exit 1. Эта «сущность» является оболочкой, которая должна быть «выше» sleep, чтобы выполнить действие. Таким образом, «настоящий» фоновый процесс - это оболочка, а sleep - это дочерний процесс.

В случае только sleep 20 & в bash есть оптимизация, что родительская оболочка в случае bash видит, что есть только одна команда, чтобы сделать. Таким образом, bash сканирует весь command command bla bla & и видит, что нужно выполнить только одну команду. Из-за этого bash вызывает только exec вместо стандартного fork+exec и становится sleep вместо выполнения дочернего процесса. Из-за exec подоболочка становится sleep, поэтому вы видите ее в имени процесса. Оптимизация ресурсов выполнена bash.

1 голос
/ 01 апреля 2020

Вы уже получаете правильную информацию о дочернем процессе. Только в вашем случае ps не знает или не хочет показывать правильное COMMAND имя для вашего подпроцесса, который вы запускаете в фоновом режиме - что, вероятно, смутило вас.

Похоже, это случай со связанными командами (.. && ..., таким образом, он не имеет ничего общего с exit 1, может быть также echo 5 et c.), где лидер группы процессов имя вместо этого отображается cmd имя.

со страницы руководства пользователя ( ps )

`cmd | COMMAND`: simple name of executable

# Process state codes
`S`: interruptible sleep (waiting for an event to complete)
`+`: is in the foreground process group

См. S+ в выводе ps | grep.

Итак, вы можете немного адаптировать свой сценарий, чтобы подтвердить, что вы действительно захватываете (d) правильную информацию о дочернем процессе, например так:

cat <<"END"> z
#! /bin/bash

sleep 20 && exit 1 &
ret=$!

echo $ret

jobs -l

# display parent and child process info
# -j Jobs format
ps -j  $$ $ret 
END

Вывод echo $ret:

30274

Вывод jobs -l:

[1]+ 30274 Running                 sleep 20 && exit 1 &

Вывод ps -j $$ $ret:

PID   PGID   SID   TTY    STAT    TIME COMMAND
30273 30273 21804 pts/0    S+     0:00 /bin/bash ./z
30274 30273 21804 pts/0    S+     0:00 /bin/bash ./z

Обратите внимание, что родитель и ребенок имеют одинаковые PGID, тогда как pid 30274 дочернего процесса, отображаемый как jobs -l и ps ..., совпадает.

Далее, если вы измените sleep 20 && exit 1 & на bash -c 'sleep 20 && exit 1' &, вы получите правильное имя команды для ребенка на этот раз, как показано ниже (ср. порядок вывода выше):

30384

[1]+ 30384 Running                 bash -c 'sleep 20 && exit 1' &

PID    PGID  SID   TTY    STAT    TIME COMMAND
30383 30383 21804 pts/0    S+     0:00 /bin/bash ./z
30384 30383 21804 pts/0    S+     0:00 bash -c sleep 20 && exit 1

Последнее, но не менее важное: в исходной версии вместо ps $ret | grep $ret вы также можете попробовать

pstree -s $ret

с pstree man page

-s: Показать родительские процессы указанного процесса.

, который предоставит вам вывод, аналогичный приведенному ниже, который также подтвердит, что вы получите правильную информацию о процессе для sleep 20 && exit 1 &:

systemd───systemd───gnome-terminal-───bash───bash───sleep
1 голос
/ 01 апреля 2020

То, что вы видите, это не родительский pid, а pid под-оболочки

При запуске:

sleep 20 && exit 1 &

Дерево процессов выглядит так:

current-shell ---> sub-shell ---> 'sleep 20 && exit 1'

Когда вы запускаете:

sleep 20 &

Дерево процессов выглядит так:

current-shell ---> 'sleep 20'

Причина, по которой вы видите pid для 'sleep 20'

...