简体   繁体   中英

How to “adopt” a child process in python

How can I get the pid of a child processes that I didn't create an object for? ie

myProc = Popen(["sleep","30"])

vs

Popen(["sleep","30"])

I've noticed they become zombie processes if I don't poll() or wait() on them after sending a termination signal. At a point in my script I would like to find all child processes that my script is a parent of and send them a signal or poll them. Is this possible in python? Is it possible at all?

You could use psutil to find the children of your parent Python process. For example:

import psutil
import os
import subprocess

subprocess.Popen(['sleep', '30'])

parent_pid = os.getpid()
parent = psutil.Process(parent_pid)

for child in parent.children():
    print(child)    # do something here

Prints:

psutil.Process(pid=16822, name='sleep')

From there you could poll them, kill them etc.

Normally, you don't need to do anything -- the current subprocess implementation maintains a global list of active unreferenced Popen instances and when a new Popen object is created, this list is enumerate and .poll() is called for each process.

Thus if you don't have a reference to the subprocess; it is waited for you automatically (and if you do have a reference then call .wait() yourself).

If child processes are created by other means then you could call os.waitpid() to collect exit statuses of dead subprocesses on Unix:

while True:
    try:
        pid, status = os.waitpid(-1, os.WNOHANG) 
    except ChildProcessError:
        #  No more child processes exist
        break
    else:
        assert pid, "child process is still alive"

On POSIX.1-2001 systems, you could call signal(SIGCHLD, SIG_IGN) to reap children automatically instead.

If you want to kill all children (send a signal) when a parent dies; you could use prctl PR_SET_PDEATHSIG on Linux . It works if the parent dies for any reason ie, it works even if the parent is killed by SIGKILL .

psutil from @ali_m' answer is a portable solution:

#!/usr/bin/env python
import gc
import subprocess
import time

import psutil


for _ in range(10):
    subprocess.Popen(['sleep', '1'])  # no reference
time.sleep(2)  # wait until they are dead
gc.collect()  # run garbage collection, to populate _active list
subprocess.Popen(['sleep', '1'])  # trigger _cleanup()

for i in range(2):
    for child in psutil.Process().children():  # immediate children
        print(child, child.status())
    if i == 0:
        time.sleep(2)

Output

psutil.Process(pid=31253, name='sleep') sleeping
psutil.Process(pid=31253, name='sleep') zombie

Note:

  1. psutil shows only one zombie process, the rest are reaped in the last Popen() call
  2. psutil provides a protection against pid reuse but it is not 100% reliable in all cases -- check whether it is enough in your case (otherwise, use one the methods above that do not rely on child's pid ).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM