How to avoid zombie processes in python?

In Python3, I have essentially the following code:

server.py:

import os
import socket

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(("127.0.0.1", 10000))
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.listen(5)

while True:
    print("waiting")
    connection, client_address = sock.accept()
    print("received")
    child_pid = os.fork()
    if child_pid == 0:
        print("connection received")
        received = connection.recv(1024)
        connection.sendall("OK".encode('utf-8'))
        os._exit(0)

      

client.py:

import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(("127.0.0.1", 10000))    
sock.close()

      

When I start the server and then the client, every time the client ends the zombie process, it remains.

How do I change the code so that the zombie process doesn't stay?

+3


source to share


2 answers


A common method is to keep track of all the children so they can be killed when the main process exits or when you want the children to be cleaned up.

You can periodically poll and fetch processes as needed, or wait until you are about to exit.

For an example of how to do this, see the collect_children () in ForkingMixin for the SocketServer module .



The os module has a number of tools for managing subprocesses like os.wait () and os.kill .

I don't know if this suits your problem or not, but multiprocessing.Pool () might be of some help. It automatically manages a pool of subprocesses and reuses them for future tasks. This is mostly useful when there is limited communication between processes and whether the work is relatively homogeneous (all child processes do the same kind of work).

+2


source


When a process exits, it remains in the process table until something reads its return code. Assuming it's Linux, you can make it a daemon and bind the init process to it. But you can also just call os.waitpid

yourself. Here is an example of a class waiting for pids in the background. Its nice because it keeps your program from exiting until it is completely picked up. You can extend it to do things like sending leak signals to child processes, logging results, etc.



import threading
import queue
import os
import time

class ZombieKiller(threading.Thread):
    """Okay, really its just a zombie waiter, but where the fun in that?
    """
    def __init__(self):
        super().__init__()
        self.pid_q = queue.Queue()
        self.start()

    def run(self):
        while True:
            pid = self.pid_q.get()
            if pid is None:
                return
            print(pid, 'wait')
            os.waitpid(pid, 0)
            print(pid, 'wait done')

    def cull_zombie(self, pid):
        self.pid_q.put(pid)

    def close(self):
        self.pid_q.put(None)
        self.join()

def test():
    zombie_killer = ZombieKiller()
    for i in range(3):
        pid = os.fork()
        if pid == 0:
            # child
            time.sleep(5)
            print(os.getpid(), 'work done')
            exit()
        else:
            # parent
            zombie_killer.cull_zombie(pid)
    zombie_killer.close()
    print('test complete')


test()

      

0


source







All Articles