Как мне найти родительский PID верхнего уровня данного процесса, используя bash? - PullRequest
19 голосов
/ 27 августа 2010

Допустим, я запускаю ps axf и вижу, что дерево процессов моей команды выглядит так:

  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

Я знаю, что могу проверить $$ для 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 верхнего уровня будет тот, который непосредственно перед тем, как я получу PID init, т. Е. 1.)

Перед тем, как написать небольшой цикл (ядаже не уверен, что для этого можно использовать рекурсию в bash), есть ли гораздо более простой метод, который мне не хватает?Может быть, просто еще один параметр файла в /proc?grep в этих файлах не обнаружил ничего очевидного.

Edit : Конечно, процесс верхнего уровня для всех процессов Linux - это / sbin / init с PID 1То, что я хочу, это PID родителя перед этим: предпоследний родитель.

Ответы [ 6 ]

11 голосов
/ 27 августа 2010

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

, а расширение фигурных скобок удаляет все до символа табуляции, оставляя только числовую часть.

10 голосов
/ 28 августа 2010

Если не найти лучшего решения, вот простой (рекурсивный) скрипт для получения родительского 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 этот скрипт и вызовите top_level_parent_pid с или без аргумента PID, в зависимости от ситуации.

Спасибо @Dennis Williamson за его многочисленные предложения о том, как написать этот сценарий компактно и эффективно.

5 голосов
/ 03 апреля 2014

Другое решение (из здесь ):

ps -p $$ -o ppid=
1 голос
/ 24 мая 2019

Я усовершенствовал рекурсивное решение вашего (@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
}
0 голосов
/ 28 апреля 2016

Итеративная версия:

# 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, а иногда - целое дерево.

0 голосов
/ 13 января 2016

Версия 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 /3855150/kak-mne-naiti-roditelskii-pid-verhnego-urovnya-dannogo-protsessa-ispolzuya-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
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...