首页 文章

为什么父进程的stdin在从分叉子进程关闭stdin文件描述符后仍然接受输入?

提问于
浏览
1

fork(2) 我正在运行的Linux系统上的手册页说明如下:

子项继承父项的打开文件描述符集的副本 . 子节点中的每个文件描述符引用相同的打开文件描述(请参阅open(2))作为父节点中的相应文件描述符 . 这意味着两个文件描述符共享打开文件状态标志,文件偏移量和信号驱动的I / O属性(请参阅fcntl(2)中的F_SETOWN和F_SET-SIG的描述) .

Python文档提到了

_exit()通常只应在fork()之后的子进程中使用 .

当然, _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() 调用提出了 EOFError .

这看起来像[child]进程写入/修改父文件描述符时,它不会影响[parent] . 也就是说,子进程获得更新的文件描述 .

那么为什么调用 _exit 作为Python Docs声明如果子进程执行的操作不影响父进程,则阻止调用清理处理程序?我们来看看 _EXIT(2) 手册页:

函数_exit()“立即”终止调用进程 . 属于该过程的任何打开的文件描述符都将被关闭;进程的任何子进程都由进程1,init继承,并且进程的父进程发送一个SIGCHLD`信号 . 函数_exit()类似于exit(3),但不调用使用atexit(3)或on_exit(3)注册的任何函数 . 打开stdio(3)流不会刷新 . 另一方面,_exit()会关闭打开的文件描述符,这可能会导致未知的延迟,等待挂起的输出完成 .

fork() 手册页没有提到子进程的清理处理程序是从父进程继承的 . 这对父母有何影响?换句话说,为什么不让孩子过程自我清理,为什么不呢?

1 回答

  • 1

    我假设你是从终端内的shell运行它 .

    shell在新进程组中启动Python进程,并使用 tcsetpgrp() 将其设置为TTY上的前台进程组 .

    一旦父Python进程终止,shell就会回收对终端的控制(它将自己设置为前台进程组) . shell不知道Python的分叉子节点仍然在运行 .

    当不属于前台进程组的进程尝试从终端读取时,它通常会收到 SIGTTIN 信号 . 但是,在这种情况下,进程组已成为孤立状态,因为其进程已终止,因此子进程在TTY上从 read() 收到 EIO 错误 . Python将其视为 EOFError .

相关问题