Подтвердить что ты не робот

Как найти исходный 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 родителя перед этим: предпоследний предок.

4b9b3361

Ответ 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
}