Python 3.6
I have a simple Python web server that, when it receives a POST request, spawns a netcat process to listen on a port.
Seems to work OK except that a zombie is left following the spawn.
(Server code below)
I send a POST request to the server like so:
curl -X POST -H "Content-Type: text/plain" --data "example data" localhost:8000
After that I do a ps ax to see the processes running and the netcat process is there, the web server is there, AND a zombie is there.
6873 pts/0 S+ 0:00 python3 nc_server.py
6876 ? Zs 0:00 [python3] <defunct>
6877 ? S 0:00 nc -l 54927
Why? How can I avoid the zombie?
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()
答案 0 :(得分:2)
当进程终止时,它期望其返回代码由其父级收集。在此之前,它不死(僵尸)地坐在那里,不再运行,但无法从进程表中清除。如果父级不再存在,则将重新建立一个进程的父进程到init(或至少在某些启用了systemd的系统IIRC上使用专用的收割进程)。在您的进程列表中,您会看到第一个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
处理程序来处理此问题。即使除了双叉守护程序之外,您通常也将使用Popen并使用subprocess与您的进程进行交互。