为啥需要关闭另一个进程的管道加上 set_close_on_exec 才能真正关闭?

Posted

技术标签:

【中文标题】为啥需要关闭另一个进程的管道加上 set_close_on_exec 才能真正关闭?【英文标题】:Why does a pipe to another process need to be closed plus set_close_on_exec to really close?为什么需要关闭另一个进程的管道加上 set_close_on_exec 才能真正关闭? 【发布时间】:2018-09-30 10:00:48 【问题描述】:

所以,我尝试使用 OCaml 与 Python 进程进行通信。我想将 Python 程序通过管道传输到 Python 解释器的标准输入,然后在 OCaml 进程中读取 Python 程序的输出。

我可以这样解决:

let py_program = |
import time

while True:
    print('hi from Python', flush=True)
    time.sleep(0.25)
|

let exec_py_program () =
  let cmd = "", [|"python3"; "-"|] in
  let pipe_out_fd, pipe_out_fd_unix = Lwt_unix.pipe_out () in

  (* Close the 1st time *)
  let () = Lwt_unix.set_close_on_exec pipe_out_fd_unix in

  let redir = `FD_move pipe_out_fd in

  let py_stream = Lwt_process.pread_lines ~stdin:redir cmd in

  let%lwt n = Lwt_unix.write_string pipe_out_fd_unix py_program 0 (String.length py_program) in
  if n < String.length py_program then failwith "Failed to write python to pipe" else

    let rec read_back () =
      match%lwt Lwt_stream.get py_stream with
      | Some str ->
        let%lwt () = Lwt_io.printl @@ "Got: " ^ str in
        read_back ()
      | None -> Lwt.return ()
    in

    (* Close the 2nd time *)
    let%lwt () = Lwt_unix.close pipe_out_fd_unix in

    read_back ()

我使用“set_close_on_exec”关闭与映射到Python进程的标准输入的管道对应的文件描述符,靠近注释“关闭第一次”,并在再次发送Python程序后再次关闭管道(“关闭第二次时间”)。 “set_close_on_exec”应该在“当进程在另一个进程上调用 exec 时”关闭文件描述符。

如果我忽略其中任何一行,Python 进程会无限期地继续从其标准输入读取数据并且永远不会开始执行,因此永远不会收到“来自 Python 的 hi”。所以我的问题是,为什么这两个都是必要的?这主要是我的猜测。

【问题讨论】:

【参考方案1】:

在 POSIX 操作系统(如 Linux)上启动程序分两步完成。首先,启动程序的进程被分叉,它创建了正在运行的进程的副本。然后,使用对exec 的调用将新进程替换为新程序。当进程被分叉时,两个生成的进程都会继承所有打开的文件描述符。因此,要真正关闭一个文件描述符,它必须在两个进程中都被关闭。

设置 close-on-exec 标志,使进程在调用exec 时立即关闭相应的文件描述符。因此,当您设置此标志时,只有旧进程在程序启动后具有打开的文件描述符。

另见this question。

【讨论】:

以上是关于为啥需要关闭另一个进程的管道加上 set_close_on_exec 才能真正关闭?的主要内容,如果未能解决你的问题,请参考以下文章

为啥线程在进程间通信期间会破坏命名管道?

为啥python在关闭fifo文件时会生成sigpipe异常?

为啥从管道读取会阻塞进程?

当我将任何命令通过管道传输到 telnet 会话时会发生啥,为啥会话会关闭

进程间通信---管道

13.无名管道通讯编程