Python 为什么父进程的stdin在关闭分叉子进程的stdin文件描述符后仍然接受输入?
Python 为什么父进程的stdin在关闭分叉子进程的stdin文件描述符后仍然接受输入?,python,linux,Python,Linux,fork(2)我正在运行的Linux系统的手册页面上说: 子级继承父级打开的文件描述符集的副本。子级中的每个文件描述符引用与父级中相应的文件描述符相同的打开文件描述(请参见打开(2))。这意味着两个文件描述符共享打开文件状态标志、文件偏移量和信号驱动I/O属性(请参阅fcntl(2)中的F_SETOWN和F_SET-SIG说明) Python文档中提到了 \u exit() 当然,\u exit不会调用清理处理程序,问题是,如果您查看以下代码: newpid = os.fork() if new
fork(2)
我正在运行的Linux系统的手册页面上说:
子级继承父级打开的文件描述符集的副本。子级中的每个文件描述符引用与父级中相应的文件描述符相同的打开文件描述(请参见打开(2))。这意味着两个文件描述符共享打开文件状态标志、文件偏移量和信号驱动I/O属性(请参阅fcntl(2)中的F_SETOWN和F_SET-SIG说明)
Python文档中提到了
\u exit()
当然,\u exit
不会调用清理处理程序,问题是,如果您查看以下代码:
newpid = os.fork()
if newpid == 0:
os.close(0)
else:
time.sleep(.25)
input()
父进程仍然接受来自stdin的输入,尽管子进程关闭了stdin。这很好,代码颠倒过来了:
newpid = os.fork()
if newpid == 0:
input()
else:
time.sleep(.25)
os.close(0)
现在,情况正好相反,这次是关闭stdin的父进程,而不是子进程。这将为子进程中的input()
调用引发eoferor
这看起来像是当[child]进程写入/修改父进程的文件描述符时,它不会影响[parent]。也就是说,子进程获取更新的文件描述
那么,如果子进程执行的操作不影响父进程,为什么要像Python文档所述调用\u exit
,以防止调用清理处理程序呢?让我们来看看<代码>退出(2)< /代码> man页面:
函数\u exit()会“立即”终止调用过程。属于进程的任何打开的文件描述符都将关闭;进程的任何子进程都由进程1 init继承,进程的父进程被发送一个
SIGCHLD`信号
函数\u exit()
类似于exit(3)
,但不调用在atexit(3)或退出时注册的任何函数。未冲洗开放式stdio(3)流。另一方面,\u exit()
fork()
manual页面没有提到子进程的清理处理程序是从父进程继承的。这对父母有什么影响?换句话说,为什么不让子进程自己清理,为什么不呢?我假设您是从终端中的shell运行这个程序的
shell在一个新的进程组中启动Python进程,并使用tcsetpgrp()
将其设置为TTY上的前台进程组
一旦父Python进程终止,shell将收回对终端的控制权(它将自己设置为前台进程组)。shell不知道Python中的分叉子级仍在运行
当不属于前台进程组的进程尝试从终端读取时,它通常会收到SIGTTIN
信号。但是,在这种情况下,进程组已被孤立,因为它的前导已终止,因此子进程从TTY上的read()
获得EIO
错误。Python将此视为eoferor
eoferor
不是由于os.close(0)
造成的。即使您使用time.sleep(5)
,也会立即得到它。如果stdin被重定向到管道或文件,也不会发生这种情况。错误发生在parent进程终止之前。延长时间。sleep
在父级退出之前,您将立即看到eoferor
。此外,如果将其更改为print(input())
,您将看到它在报告错误后成功读取输入。@Barmar这不是我在Linux机器上观察到的行为。@Barmar OP在Linux上。@direprobs噢,实际上我需要调整一下我的答案<如果进程组是后台的,则会发生code>sigtin
,但在这种情况下,您的孩子现在是孤立进程组的一部分,这意味着从控制台读取将导致EIO
,Python将其转换为eoferor
。