Skip to content

Commit

Permalink
Issue giampaolo#1183: implement ppid_map for Linux
Browse files Browse the repository at this point in the history
This makes Process.children() 3x faster when there are no children.
  • Loading branch information
pitrou committed Nov 30, 2017
1 parent 7c6b6c2 commit 0482236
Showing 1 changed file with 50 additions and 13 deletions.
63 changes: 50 additions & 13 deletions psutil/_pslinux.py
Original file line number Diff line number Diff line change
Expand Up @@ -1356,6 +1356,24 @@ def pid_exists(pid):
return pid in pids()


def parse_stat_file(procfs_path, pid):
"""Parse /proc/{pid}/stat file. Return a list of fields where
process name is in position 0.
Using "man proc" as a reference: where "man proc" refers to
position N, always substract 2 (e.g starttime pos 22 in
'man proc' == pos 20 in the list returned here).
"""
with open_binary("%s/%s/stat" % (procfs_path, pid)) as f:
data = f.read()
# Process name is between parentheses. It can contain spaces and
# other parentheses. This is taken into account by looking for
# the first occurrence of "(" and the last occurence of ")".
rpar = data.rfind(b')')
name = data[data.find(b'(') + 1:rpar]
others = data[rpar + 2:].split()
return [name] + others


def wrap_exceptions(fun):
"""Decorator which translates bare OSError and IOError exceptions
into NoSuchProcess and AccessDenied.
Expand All @@ -1381,6 +1399,35 @@ def wrapper(self, *args, **kwargs):
return wrapper


def ppid_map():
"""
Return a {pid:ppid, ...} dict for all running processes
"""
procfs_path = get_procfs_path()
ppids = {}

for pid in os.listdir(procfs_path):
if pid.isdigit():
pid = int(pid)
try:
fields = parse_stat_file(procfs_path, pid)
except EnvironmentError as err:
# Ignore dead processes
# ESRCH (no such process) can be raised on read() if
# process is gone in the meantime.
# ENOENT (no such file or directory) can be raised on open().
if err.errno in (errno.ESRCH, errno.NOENT):
pass
elif err.errno in (errno.EPERM, errno.EACCES):
raise AccessDenied(pid)
else:
raise
ppid = int(fields[2])
ppids[pid] = ppid

return ppids


class Process(object):
"""Linux process implementation."""

Expand All @@ -1395,22 +1442,12 @@ def __init__(self, pid):
@memoize_when_activated
def _parse_stat_file(self):
"""Parse /proc/{pid}/stat file. Return a list of fields where
process name is in position 0.
Using "man proc" as a reference: where "man proc" refers to
position N, always substract 2 (e.g starttime pos 22 in
'man proc' == pos 20 in the list returned here).
process name is in position 0, as in parse_stat_file().
The return value is cached in case oneshot() ctx manager is
in use.
"""
with open_binary("%s/%s/stat" % (self._procfs_path, self.pid)) as f:
data = f.read()
# Process name is between parentheses. It can contain spaces and
# other parentheses. This is taken into account by looking for
# the first occurrence of "(" and the last occurence of ")".
rpar = data.rfind(b')')
name = data[data.find(b'(') + 1:rpar]
others = data[rpar + 2:].split()
return [name] + others
return parse_stat_file(self._procfs_path, self.pid)

@memoize_when_activated
def _read_status_file(self):
Expand Down

0 comments on commit 0482236

Please sign in to comment.