Как найти исходный PID верхнего уровня для данного процесса с помощью bash?
Скажем, я запустил ps axf
, и я вижу, что мое дерево процессов процесса выглядит следующим образом:
800 ? Ss 0:00 /usr/sbin/sshd
10186 ? Ss 0:00 \_ sshd: yukondude [priv]
10251 ? S 0:00 \_ sshd: [email protected]/0
10252 pts/0 Ss 0:00 \_ -bash
10778 pts/0 S 0:00 \_ su -
10785 pts/0 S 0:00 \_ -su
11945 pts/0 R+ 0:00 \_ ps axf
Я знаю, что могу проверить $$
для текущего идентификатора PID (10785) или $PPID
для родительского PID (10778).
Но я просто хочу родительский PID верхнего уровня, который в этом примере будет 800 (демон SSH). Есть ли способ сделать это легко?
Я узнал из этого SO ответ, что я могу рекурсивно проверить 4-ю запись в файле /proc/PID/stat
, чтобы найти каждый родительский PID процесса:
# cut -f4 -d' ' /proc/10785/stat
10778
# cut -f4 -d' ' /proc/10778/stat
10252
# cut -f4 -d' ' /proc/10252/stat
10251
# cut -f4 -d' ' /proc/10251/stat
10186
# cut -f4 -d' ' /proc/10186/stat
800
# cut -f4 -d' ' /proc/800/stat
1
(родительский PID верхнего уровня будет таким, как только я доберусь до init
PID, т.е. 1.)
Прежде чем писать небольшой цикл (я даже не уверен, что вы можете использовать рекурсию в bash), есть ли гораздо более простой метод, который мне не хватает? Может быть, просто еще один параметр файла под /proc
? A grep
через эти файлы не выявил ничего очевидного.
Изменить. Конечно, процесс верхнего уровня для всех процессов Linux - это /sbin/init с ПИД-кодом 1. Я хочу, чтобы PID родителя перед этим: предпоследний предок.
Ответы
Ответ 1
Невыполнение лучшего решения, здесь простой (рекурсивный) script, чтобы получить родительский PID верхнего уровня любого номера процесса, который вы ему даете (или текущую оболочку, если вы не указываете аргумент PID):
#!/bin/bash
# Look up the top-level parent Process ID (PID) of the given PID, or the current
# process if unspecified.
function top_level_parent_pid {
# Look up the parent of the given PID.
pid=${1:-$$}
stat=($(</proc/${pid}/stat))
ppid=${stat[3]}
# /sbin/init always has a PID of 1, so if you reach that, the current PID is
# the top-level parent. Otherwise, keep looking.
if [[ ${ppid} -eq 1 ]] ; then
echo ${pid}
else
top_level_parent_pid ${ppid}
fi
}
Просто source
этот script и вызовите top_level_parent_pid
с аргументом PID или без него.
Благодаря @Dennis Williamson за его многочисленные предложения о том, как написать этот script компактно и эффективно.
Ответ 2
Баш может определенно сделать рекурсию.
Вы можете извлечь четвертое поле из файла статистики, не используя утилиту внешней cut
, выполнив что-то вроде этого:
stat=($(</proc/$$/stat)) # create an array
ppid=${stat[3]} # get the fourth field
Если в имени команды может быть пробел (ы), вы можете считать с конца массива (при условии, что количество полей стабильно). Это также будет работать, если в имени команды нет пробелов.
ppid=${stat[-49]} # gets the same field but counts from the end
Вот еще один метод, который должен избежать этих проблем (но может дать сбой, если имя команды содержит символ новой строки):
mapfile -t stat < /proc/$$/status
ppid=${stat[5]##*$'\t'}
Пятое поле в этом файле выглядит так:
PPid: 1234
и расширение скобки удаляет все до символа табуляции, оставляя только числовую часть.
Ответ 3
Другое решение (из здесь):
ps -p $$ -o ppid=
Ответ 4
версия OS X, адаптированная из ответов @albert и @yukondude:
#!/usr/bin/env bash
# Look up the top-level parent Process ID (PID) of the given PID, or the current
# process if unspecified.
# From http://stackoverflow.com/questions/3586888/how-do-i-find-the-top-level-parent-pid-of-a-given-process-using-bash
function top_level_parent_pid {
# Look up the parent of the given PID.
PID=${1:-$$}
PARENT=$(ps -p $PID -o ppid=)
# /sbin/init always has a PID of 1, so if you reach that, the current PID is
# the top-level parent. Otherwise, keep looking.
if [[ ${PARENT} -eq 1 ]] ; then
echo ${PID}
else
top_level_parent_pid ${PARENT}
fi
}
Ответ 5
Итеративная версия:
# ppid -- Show parent PID
# $1 - The process whose parent you want to show, default to $$
function ppid() {
local stat=($(</proc/${1:-$$}/stat))
echo ${stat[3]}
}
# apid -- Show all ancestor PID
# $1 - The process whose ancestors you want to show, default to $$
# $2 - Stop when you reach this ancestor PID, default to 1
function apid() {
local ppid=$(ppid ${1:$$})
while [ 0 -lt $ppid -a ${2:-1} -ne $ppid ]; do
echo $ppid
ppid=$(ppid $ppid)
done
}
Как две отдельные функции, потому что иногда вам нужен только родительский PID, а иногда вам нужно все дерево.
Ответ 6
Я усовершенствовал ваше рекурсивное решение (@yukondude), чтобы избежать проблем, когда имя команды содержит символы внутреннего разделителя полей (IFS), такие как пробел, табуляция и символ новой строки, которые являются допустимыми символами имени файла Unix.
#!/bin/bash
# Look up the top-level parent Process ID (PID) of the given PID, or the current
# process if unspecified.
function top_level_parent_pid {
# Look up the parent of the given PID.
pid=${1:-$$}
ppid="$(awk '/^PPid:/ { print $2 }' < /proc/"$pid"/status)"
# /sbin/init always has a PID of 1, so if you reach that, the current PID is
# the top-level parent. Otherwise, keep looking.
if [[ ${ppid} -eq 1 ]] ; then
echo "${pid}"
else
top_level_parent_pid "${ppid}"
fi
}