上面的结构完美符合我们最初的场景: p1 是操作系统的 1 号进程 init,p2 是 bash shell,p3 是 strace(ctrl-c 之后进程死掉),p4 是 python repl。这个结构恐怕很难简化,如果有哪位老师发现 3 进程可以复现请教教我
运行 docker run --name repl -td -v $(pwd):/src python:3.14.0rc2 python /src/main.py,观察到下面的进程树。
strace -p 最新的 python 进程,确认无限 EIO,QED

#!/usr/bin/env python3
import os, sys, pty, fcntl, termios, signal, time
def handler(sig, f):
pass
def spawn_p2():
master_fd, slave_fd = pty.openpty()
os.setsid()
fcntl.ioctl(slave_fd, termios.TIOCSCTTY, 0)
p2_pgid = os.getpgrp()
pid3 = os.fork()
if pid3 == 0:
os.setpgid(0, 0)
os.dup2(slave_fd, 0)
os.dup2(slave_fd, 1)
os.dup2(slave_fd, 2)
if slave_fd > 2:
os.close(slave_fd)
pid4 = os.fork()
if pid4 == 0:
signal.signal(signal.SIGTTOU, handler)
exe = 'python'
os.execlp(exe, os.path.basename(exe))
else:
os.tcsetpgrp(0, p2_pgid)
os._exit(0)
try:
os.setpgid(pid3, pid3)
except ProcessLookupError:
pass
os.tcsetpgrp(slave_fd, pid3)
if slave_fd > 2:
os.close(slave_fd)
os.waitpid(pid3, 0)
time.sleep(1)
os.tcsetpgrp(master_fd, p2_pgid)
while 1:
os.read(master_fd, 1024)
def main():
pid2 = os.fork()
if pid2 == 0:
spawn_p2()
else:
time.sleep(1 << 30)
if __name__ == "__main__":
main()运行 docker run --name repl -td -v $(pwd):/src python:3.14.0rc2 python /src/main.py,观察到下面的进程树。
root 67924 67901 1 13:49 pts/0 00:00:00 \_ python /src/orp_repl2.py
root 67977 67924 1 13:49 pts/1 00:00:00 \_ python /src/orp_repl2.py
root 67979 67924 79 13:49 pts/1 00:00:02 \_ pythonstrace -p 最新的 python 进程,确认无限 EIO,QED