如何获取进程的祖父进程ID

8

如何获取当前进程的父进程ID?
一般来说,给定一个进程ID,如何获取其父进程ID?
例如,可以使用os.getpid()获取进程ID,使用os.getppid()获取父进程ID,那么如何获取祖先进程ID呢?

我的目标是Linux(Ubuntu),因此平台特定的答案也可以。


1
是的,更好的方法是os.pnid(pid, N)或者只使用os.getppid(pid)。 - Anurag Uniyal
7个回答

24

通过使用 psutil (https://github.com/giampaolo/psutil):

>>> import psutil
>>> psutil.Process().ppid()
2335
>>> psutil.Process().parent()
<psutil.Process (pid=2335, name='bash', cmdline='bash') at 140052120886608>
>>> 

1
这是更可靠的答案,psutil 真是救命稻草。 - blented
1
应该使用 os.getppid() 而不是 os.getpid()。使用 os.getppid() 代替 psutil.Process(os.getpid()).ppid()(返回父进程的PID)。如果OP要求获取进程的祖父进程ID,则使用psutil.Process(os.getppid()).ppid()(请注意:getppid()而不是getpid())。 - jfs

7

Linux 特定:

os.popen("ps -p %d -oppid=" % os.getppid()).read().strip()

自然地,如果你没有安装cygwin,那在Windows上是行不通的。 - ddaa

5
我认为你无法用便携式的Python方法来完成这个任务,但有两种可能性。
  1. 该信息可以从ps命令中获取,因此您可以分析它。
  2. 如果您有一个带有proc文件系统的系统,则可以打开文件/proc/<pid>/status并搜索包含PPid:的行,然后对该PID执行相同操作。
例如,以下脚本将获取您的PID、PPID和PPPID(如果权限允许):
#!/bin/bash
pid=$$
ppid=$(grep PPid: /proc/${pid}/status | awk '{print $2'})
pppid=$(grep PPid: /proc/${ppid}/status | awk '{print $2'})
echo ${pid} ${ppid} ${pppid}
ps -f -p "${pid},${ppid},${pppid}"

生成:

3269 3160 3142
UID        PID  PPID  C STIME TTY          TIME CMD
pax       3142  2786  0 18:24 pts/1    00:00:00 bash
root      3160  3142  0 18:24 pts/1    00:00:00 bash
root      3269  3160  0 18:34 pts/1    00:00:00 /bin/bash ./getem.sh

显然,你需要使用Python打开这些文件。

+1个好的工作示例,但是pixelbeat的答案更容易实现,有什么需要注意的吗? - Anurag Uniyal
不,pixelbeat的代码看起来很好,事实上,我已经点赞了,因为它比我的方法稍微简单一些 - 我不知道ps有那些选项,因为我来自非常早期的UNIX时代 :-) - paxdiablo
我编辑了你的答案(ps行)。使用grep过滤ps输出可能会导致不正确的结果(例如,考虑pppid为1的情况)。 顺便说一句,-p选项非常老旧。 - tzot
没问题,ΤΖΩΤΖΙΟΥ,那只是为了展示这三个数字是正确的,不是答案的一部分。但从现在开始我会使用 -p 选项代替 grep。 - paxdiablo

3
from __future__ import with_statement

def pnid(pid=None, N=1):
    "Get parent (if N==1), grandparent (if N==2), ... of pid (or self if not given)"
    if pid is None:
        pid= "self"

    while N > 0:
        filename= "/proc/%s/status" % pid
        with open(filename, "r") as fp:
            for line in fp:
                if line.startswith("PPid:"):
                    _, _, pid= line.rpartition("\t")
                    pid= pid.rstrip() # drop the '\n' at end
                    break
            else:
                raise RuntimeError, "can't locate PPid line in %r" % filename
        N-= 1

    return int(pid) # let it fail through


>>> pnid()
26558
>>> import os
>>> os.getppid()
26558
>>> pnid(26558)
26556
>>> pnid(N=2)
26556
>>> pnid(N=3)
1

顺便说一句,我经常有人问我为什么不遵循PEP-8,在给变量赋值时从不在名称后面加空格;这是我在编写C代码时养成的老习惯,一直保持至今;多年来,我已经很久没有因为“=”而被咬到“==”错误(或语法错误)了。 - tzot
+1 对于 pnid,但是递归的 "ps -p %d -oppid=" % pid 会更短更清晰。 - Anurag Uniyal
1
这是一个单进程 Python 脚本;我认为递归的 ps 更适合用作 shell 脚本。我不将 Python 视为通用的 /bin/sh 替代品。无论如何,感谢您在评论中的“+1”。 - tzot

0

我为了一个作业查找了这个问题,但没有找到我想要的答案,所以我会在这里发布。我知道这很明显,但它让我困惑了一会儿。如果你是编写祖父代码的人,你可以这样做:

#include <stdio.h>
#include <sys/types.h>
#include<sys/wait.h>
#include <unistd.h>

int main(void) {
  int grandpa = getpid();
  int id = fork();
  if (id == 0) {
    int id2 = fork();
    if (id2 == 0) {
      printf("I am the child process C and my pid is %d. My parent P has pid %d. My grandparent G has pid %d.\n", getpid(), getppid(), grandpa);
    } else {
      wait(NULL);
      printf("I am the parent process P and my pid is %d. My parent G has pid %d.\n", getpid(), getppid());
    }
  } else {
    wait(NULL);
    printf("I am the grandparent process G and my pid is %d.\n", getpid());
  }
}

0

我认为在一般情况下你无法以可移植的方式完成这个任务。

你需要从进程列表中获取这些信息(例如通过ps命令),这是以系统特定的方式获得的。


他似乎不关心可移植性。 - Matt Joiner

-1
如果您有一个符合POSIX标准的“ps”命令,可以指定您想要的列,例如: ps -o pid,ppid 然后您可以尝试:
import os
import re

ps = os.popen("ps -o pid,ppid")
ps.readline()    # discard header
lines = ps.readlines()
ps.close

procs = [ re.split("\s+", line.strip()) for line in lines ]

parent = {}
for proc in procs:
    parent[ int(proc[0]) ] = int(proc[1])

现在你可以做:

parent[ parent[pid] ]

你甚至可以编写一个函数来列出进程的祖先:
def listp(pid):
    print(pid)
    if parent.has_key(pid):
        listp( parent[pid] )

2
line.split() 将会代替 re.split(r"\s+", line.strip())。默认的 split() 会在 \s+ 处分割并移除前后空格。 - Andrew Dalke

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接