【问题标题】:python pty.fork - how does it workpython pty.fork - 它是如何工作的
【发布时间】:2011-04-30 16:07:48
【问题描述】:

http://docs.python.org/library/pty.html 说 -

pty.fork()¶ 叉。将孩子的控制终端连接到伪终端。返回值为 (pid, fd)。请注意,孩子的 pid 为 0,并且 fd 无效。 parent 的返回值是 child 的 pid,fd 是连接到 child 的控制终端(也连接到 child 的标准输入和输出)的文件描述符。

这是什么意思?每个进程都有 3 个 fd(stdin、stdout、stderr)。 现在这会影响这些 fds 吗?子进程不会有这些 fd 吗?我很困惑。--完全。

【问题讨论】:

    标签: python pty


    【解决方案1】:

    我想我终于得到了 Python 中pty.fork 的最小示例 - 由于我发现很难找到类似的示例,因此我将其发布在这里作为@joni 答案的说明。它本质上是基于:

    特别讨厌的部分是查找仍然引用已过时的master_open() 的文档;并且pty.fork不会 生成子进程这一事实,除非文件描述符(由 fork 方法返回)被父进程读取! (请注意,os.fork 中没有这样的要求)另外,os.fork 似乎更便携(阅读一些 cmets,注意到 pty.fork 在某些平台上不起作用)。

    无论如何,这里首先是一个充当可执行文件的脚本 (pyecho.py)(它只是从标准输入中读取行,然后以大写形式写回):

    #!/usr/bin/env python
    # pyecho.py
    
    import sys;
    
    print "pyecho starting..."
    
    while True:
      print sys.stdin.readline().upper()
    

    ...然后,这是实际的脚本(它要求 pyecho.py 位于同一目录中):

    #!/usr/bin/env python
    
    import sys
    import os
    import time
    import pty
    
    def my_pty_fork():
    
      # fork this script
      try:
        ( child_pid, fd ) = pty.fork()    # OK
        #~ child_pid, fd = os.forkpty()      # OK
      except OSError as e:
        print str(e)
    
      #~ print "%d - %d" % (fd, child_pid)
      # NOTE - unlike OS fork; in pty fork we MUST use the fd variable
      #   somewhere (i.e. in parent process; it does not exist for child)
      # ... actually, we must READ from fd in parent process...
      #   if we don't - child process will never be spawned!
    
      if child_pid == 0:
        print "In Child Process: PID# %s" % os.getpid()
        # note: fd for child is invalid (-1) for pty fork!
        #~ print "%d - %d" % (fd, child_pid)
    
        # the os.exec replaces the child process
        sys.stdout.flush()
        try:
          #Note: "the first of these arguments is passed to the new program as its own name"
          # so:: "python": actual executable; "ThePythonProgram": name of executable in process list (`ps axf`); "pyecho.py": first argument to executable..
          os.execlp("python","ThePythonProgram","pyecho.py")
        except:
          print "Cannot spawn execlp..."
      else:
        print "In Parent Process: PID# %s" % os.getpid()
        # MUST read from fd; else no spawn of child!
        print os.read(fd, 100) # in fact, this line prints out the "In Child Process..." sentence above!
    
        os.write(fd,"message one\n")
        print os.read(fd, 100)        # message one
        time.sleep(2)
        os.write(fd,"message two\n")
        print os.read(fd, 10000)      # pyecho starting...\n MESSAGE ONE
        time.sleep(2)
        print os.read(fd, 10000)      # message two \n MESSAGE TWO
        # uncomment to lock (can exit with Ctrl-C)
        #~ while True:
          #~ print os.read(fd, 10000)
    
    
    if __name__ == "__main__":
        my_pty_fork()
    

    嗯,希望这对某人有所帮助,
    干杯!

    【讨论】:

      【解决方案2】:

      使用 pty.fork() 的主要目的是返回的伪终端 (pty) 文件描述符可用于以不同的方式与生成的进程通信,即。通过直接写入和读取其(伪)终端 - 而不是 stdin/out/err。

      还有更多信息about pty's and tty's(来自 StackOverflow)和a link to a simple example of using pty.fork()

      【讨论】:

        【解决方案3】:

        "and fd is a file descriptor connected to child's control terminal" --> 子进程看不出有什么区别,可以正常访问stdin/out(stderr我不知道)。唯一的区别是“管道”的另一端不是用户正在阅读/打字的终端,而是可以访问的父进程是返回的fd。

        【讨论】:

          【解决方案4】:

          当使用pty.fork()时,子进程被告知它正在写入一个实际的终端,一个tty,就像你通常使用的一样。但是,它正在写入一个pty,一个伪终端,这是一个由另一个程序控制的 tty。

          只有一个 fd,因为子程序正在向终端写入它想要的东西。这是标准输出、标准错误和任何终端转义码的组合。 stdout/stderr 在这种情况下没有任何意义,因为它们被打印到终端,并且当程序连接到 pty 时它们不能单独访问(就像当你阅读程序的输出时你无法分辨哪个流是哪个)。

          如果需要,您仍然可以将 stdout 或 stderr 重定向到文件。这将在孩子运行的代码的分叉部分中完成。您可以重定向其标准流或重定向子流程的流。

          这是一个基于 sdaau 答案的示例程序(他们的答案在 Python3 中不起作用)。

          #!/usr/bin/env python3
          
          import sys
          import os
          import time
          import pty
          import subprocess
          
          
          def log(chars):
              sys.stdout.write("    > " + chars + "\n")
          
          
          def main():
          
              # fork this script such that a child process writes to a pty that is
              # controlled or "spied on" by the parent process
          
              (child_pid, fd) = pty.fork()
          
              # A new child process has been spawned and is continuing from here.
              # The original parent process is also continuing from here.
              # They have "forked".
          
              if child_pid == 0:
                  log("This is the child process fork, pid %s" % os.getpid())
                  log("Child process will run a subprocess controlled by the parent process")
                  log("All output, including this text, will be written to a pty and handled ")
                  log("by the parent process.")
                  # redirect stdout/stderr if you want to here
                  subprocess.run(["bash"])
          
              else:
                  log("This is the parent process fork, pid %s" % os.getpid())
                  log("the fd being read from, %s, is not stdout nor stderr; it is " % fd)
                  log("simply what the child is trying to write to its tty. ")
                  log("stdout/stderr are combined along with terminal escape codes.")
          
                  print()
                  # Read initial output of child process before "typing" anything in its pty
                  sys.stdout.write(os.read(fd, 1024).decode())
                  print()
          
                  # Run any bash commands you want. I/O to the fd is handled as if you are typing
                  # at a terminal.
                  os.write(fd, "ls\n".encode())
                  os.write(fd, "which git\n".encode())
                  # you can even test tab completions
                  os.write(fd, "git sta\t\t".encode())
                  while True:
                      log("parent will read 1024 bytes that the child wrote to its pty")
                      log("if no new output is available, parent will wait. Exit with ctrl+c.\n")
                      # take out decode() to see raw bytes the child wrote to its pty
                      sys.stdout.write(os.read(fd, 1024).decode())
                      time.sleep(1)
          
          
          if __name__ == "__main__":
              main()
          

          【讨论】:

            【解决方案5】:

            谢谢 Joni。这就是我所理解的。当 pty.fork() 被调用时。父进程连接到 ptmx master。 parent 将等待来自键盘的输入或来自 master 的数据。

            child 关闭其标准输入、标准输出和标准错误。并复制从属 stdin、stdout.stderr。 现在孩子执行了一个程序(比如 bc)。程序正在等待输入,当你输入 1+1 时——它被孩子/奴隶传递给主人(记住孩子和奴隶都有一些标准输入,标准输出,标准错误)。 master 计算它的答案“2”并写入 stdout——因为父级正在等待来自 master 的数据——它选择“2”并写入 stdout。

            我在伪终端上浏览了几个不错的旧 c 程序后得出了这个结论 :) 我认为 python 的逻辑不会与它们不同。 HTH 某人。

            【讨论】:

              猜你喜欢
              • 2012-04-29
              • 1970-01-01
              • 2010-12-27
              • 1970-01-01
              • 2017-03-19
              • 1970-01-01
              • 1970-01-01
              • 2017-07-09
              • 2021-09-26
              相关资源
              最近更新 更多