Let's say I run ps axf
and I can see that my command's process tree looks like this:
800 ? Ss 0:00 /usr/sbin/sshd
10186 ? Ss 0:00 \_ sshd: yukondude [priv]
10251 ? S 0:00 \_ sshd: yukondude@pts/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
I know I can check $$
for the current shell's PID (10785) or $PPID
for the parent PID (10778).
But I just want the top-level parent PID, which would be 800 (SSH daemon) in this example. Is there any way to do that easily?
I learned from this SO answer that I can recursively check the 4th entry in the /proc/PID/stat
file to find each process's parent 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
(The top-level parent PID will be the one just before I reach init
's PID, i.e., 1.)
Before I write a little loop (I'm not even sure if you can use recursion in bash) to do this, is there a much more straightforward method that I'm missing? Maybe just another parameter of a file under /proc
? A grep
through those files didn't reveal anything obvious.
Edit: Of course, the top-level process for all Linux processes is /sbin/init with a PID of 1. What I want is the PID of the parent just before that: the penultimate parent.
Если нет лучшего решения, вот простой (рекурсивный) скрипт для получения родительского 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
}
Просто используйте
этот скрипт и вызовите top_level_parent_pid
с аргументом PID или без него, в зависимости от ситуации.
Спасибо @Dennis Williamson за его многочисленные предложения о том, как написать этот сценарий компактно и эффективно.
Bash определенно может выполнять рекурсию.
Вы можете получить четвертое поле из файла статистики без использования внешней утилиты 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
и раскрытие фигурной скобки удаляет все до табуляции символ, оставляя только числовую часть.