繁体   English   中英

如何在不离开僵尸进程的情况下从Python Web服务器加倍分叉?

[英]How to double fork from a Python web server without leaving a zombie process?

Python 3.6

我有一个简单的Python Web服务器,当它收到POST请求时,会产生一个netcat进程来监听端口。

似乎工作正常,除了在产卵后留下一个僵尸。

(下面的服务器代码)

我向服务器发送POST请求,如下所示:

curl -X POST -H "Content-Type: text/plain" --data "example data"  localhost:8000

之后,我做了一个ps ax,看看进程正在运行,netcat进程在那里,Web服务器在那里,还有一个僵尸。

6873 pts/0    S+     0:00 python3 nc_server.py
6876 ?        Zs     0:00 [python3] <defunct>
6877 ?        S      0:00 nc -l 54927

为什么? 我怎么能避免僵尸?

from http.server import HTTPServer, BaseHTTPRequestHandler
import socket
import os
import io

host = '0.0.0.0'
port = 8000

def find_free_port():
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.bind(('', 0))
        return s.getsockname()[1]

def spawn_netcat(port):
    command = "/bin/nc"
    params = f"nc -l {port}"
    spawnDaemon(command, params.split())

def spawnDaemon(path_to_executable, params):

    # Do first fork
    pid = os.fork()
    if (pid != 0):
        return

    # Decouple from parent environment
    os.chdir("/opt")
    os.setsid()
    os.umask(0)

    # Do second fork
    pid = os.fork()
    if (pid != 0):
        os._exit(0)

    # exec this process into netcat.....
    os.execv(path_to_executable, params)

class Server(BaseHTTPRequestHandler):

    def do_POST(self):
        netcat_listen_port = find_free_port()
        spawn_netcat(netcat_listen_port)
        self.send_response(200)
        self.send_header("Content-type", "text/plain")
        self.end_headers()
        response = io.BytesIO()
        response.write(str(netcat_listen_port).encode())
        self.wfile.write(response.getvalue())

if __name__ == "__main__":
    httpd = HTTPServer((host, port), Server)
    httpd.serve_forever()

当进程终止时,它期望其父代收集其返回代码。 在此之前,它就位于亡灵(僵尸)中,它不再运行,但它无法从进程表中清除。 如果父进程不再存在,则进程将重新成为init(或至少某些已启用systemd的系统IIRC上的专用reaper进程)。 在您的进程列表中,您将看到第一个fork的子级,其中包含其父级,但未收集返回状态。

长话短说,经过第一个fork() ,你可以在父进程return之前插入它:

os.waitid(os.P_PID, pid, os.WEXITED)

即:

# Do first fork
pid = os.fork()
if (pid != 0):
    os.waitid(os.P_PID, pid, os.WEXITED)
    return

这个孩子只是短暂的并且退出。 在返回函数之前,它会等待它执行此操作。 在这种情况下应该没问题。 否则,一般情况下,如果您的进程产生可能在以后退出的子进程,那么您将注册一个SIGCHLD处理程序来处理此问题。 即使除了双叉到daemonize之外,你通常会使用Popen并使用进程与你的进程接口。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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