键盘输入是否总是通过控制终端?

ana*_*nik 2 terminal ioctl controlling-terminal

从键盘输入的所有输入都通过控制终端是否正确?这意味着如果程序在没有控制终端的情况下运行,它将无法接收任何用户输入。这是否适用于 Linux 中的每种程序?

更新 #1:为了澄清这个问题,当 stdin 被重定向时,我的 Python 寻呼机模块崩溃了

$ ./pager.py < README.rst
...
  File "pager.py", line 566, in <module>
    page(sys.stdin)
  File "pager.py", line 375, in page
    if pagecallback(pagenum) == False:
  File "pager.py", line 319, in prompt
    if getch() in [ESC_, CTRL_C_, 'q', 'Q']:
  File "pager.py", line 222, in _getch_unix
    old_settings = termios.tcgetattr(fd)
termios.error: (25, 'Inappropriate ioctl for device')
Run Code Online (Sandbox Code Playgroud)

这是因为我尝试获取描述符以将键盘输入设置为fd = sys.stdin.fileno(). 当stdin被重定向,它的文件描述符不再与任何键盘输入相关联,因此试图设置它失败input-output control的错误。

我被告知要得到这个controlling terminal,但我不知道它来自哪里。我知道这是一种从用户向正在运行的进程发送信号的通道,但同时也可以在没有它的情况下运行进程。

所以问题是 - 我应该总是从 读取我的键盘输入controlling terminal吗?如果在没有它的情况下运行寻呼机进程会发生什么?键盘输入对用户仍然重要吗?我应该关心从其他来源获取它吗?

Sté*_*las 9

不。终端应用程序从与您正在键入的键盘的终端对应的设备文件(在 Linux 上,类似于/dev/ttyS0/dev/ttyUSB0... 对于串行设备,/dev/pts/0对于伪终端设备)读取键盘输入。

该设备不必是进程(或任何与此相关的进程)的控制终端

您可以这样做,cat /dev/pts/x前提是您对该设备文件具有读取权限,并且可以读取另一端终端(如果有)上键入的内容。

实际上,如果它是进程的控制终端并且该进程不在该终端的前台进程组中,则该进程通常会在尝试从中读取时被挂起(如果它在前台进程组中,它如果发送将获得SIGINT / SIGTSTP / SIGQUIT一个^C/ ^Z/^\无论过程是否从终端设备读取或没有)。如果终端设备不是进程的控制终端(如果进程是不同会话的一部分),这些事情就不会发生。这就是控制终端的意义所在。用于作业控制由交互式 shell 实现的机制。除了那些 SIGTTIN/SIGTTOU 和 SIGINT/SIGTSTP/SIGQUIT 信号,控制终端在终端挂起时参与 SIGHUP 的传递,它也是/dev/tty重定向到的 tty 设备。

在任何情况下,这仅适用于终端输入:真实的就像在通过串行电缆连接的终端设备中一样,像 X11 终端模拟器一样模拟,例如 xterm使用伪终端设备,或者由内核模拟,例如 Linux 上的虚拟终端与进程交互/dev/tty<x>(并支持超过标准的终端接口)。

X 服务器等应用程序通常从键盘驱动程序获取键盘输入。在 Linux 上使用通用输入抽象层。X 服务器又提供了一种事件机制,将键盘事件与连接到它的应用程序进行通信。例如,xterm将接收 X11 键盘事件,将其转换为将字符写入伪终端设备的主端,这将转换为在“内部”运行的进程在xterm从相应的伪终端从设备读取时读取相应的字符 ( /dev/pts/x) .

现在,没有终端应用程序这样的东西。上面我们所说的终端应用程序是通常在终端中使用的应用程序,这些应用程序期望在终端中显示并从终端(如vi、交互式 shell 或 )获取输入less。但是任何应用程序都可以由终端控制,任何读取或写入文件或其 stdin/stdout/stderr 的应用程序都可以执行到终端设备的 I/O。

例如,如果您运行firefox,一个连接到 X 服务器以进行用户 I/O 的应用程序,从在 中运行的 shell 内xterm,将从它的 shell 父级firefox继承控制终端^C如果它是由 shell 在前台启动的,则在终端中会杀死它。它还将在该文件上打开其文件描述符 0、1 和 2(stdin、stdout 和 stderr)/dev/pts/<x>(再次从其父壳继承)。并且firefox很可能最终会因为某种错误而在 fd 2 (stderr) 上写入(如果它被置于后台并且终端设备配置了stty tostop,那么它会收到一个 SIGTTOU 并被挂起)。

如果相反,它firefox是由您的 X 会话管理器或 Windows 管理器启动的(当您单击某个菜单上的某个 Firefox 图标时),它可能不会获得任何控制终端,并且不会有任何文件描述符连接到任何(您会看到ps -fp <firefox-pid>显示?作为ttylsof -p <firefox-pid>显示没有文件描述符/dev/pts/*/dev/tty*)。但是file:///dev/pts/<x>,如果您浏览到,firefox仍然可以对终端设备进行一些 I/O。如果它在没有O_NOCTTY标志的情况下打开该文件,并且它碰巧是会话领导者,并且还/dev/pts/<x>没有附加会话,则该设备最终将成为该firefox进程的控制终端。

更多阅读:

编辑

在您的编辑澄清了一些问题并添加了一些上下文之后。

上面应该清楚地表明,一个进程可以从他们喜欢的任何终端设备读取输入(如果进程不在其前台进程组中,则控制终端除外),但这并不是你真正感兴趣的。

您的问题是:对于交互式终端应用程序,当 stdin 不再指向终端时,从何处获取用户输入。

应用程序喜欢tr从标准输入获取输入并在标准输出上写入。当 stdin/stdout 是在另一端带有终端的 tty 设备时,它们碰巧是交互式的,因为它们从/向用户读取和写入数据。

当 stdin 不再是终端时,某些终端文本编辑器(例如ed/ex甚至某些vi实现)会继续从 stdin 读取它们的输入,因此它们可以编写脚本。

寻呼机虽然是一个典型的应用程序,但即使他们的输入不是终端(至少当他们的输出仍然进入终端时),它仍然需要与用户交互。因此,他们需要另一个信道给所述终端装置采取的用户输入。问题是:他们应该使用哪种终端设备?

是的,它应该是控制终端。因为这通常是控制终端的意思。当您按下 Ctrl-C/Z 时,该设备会向寻呼机发送 SIGINT/SIGTSTP,因此寻呼机从同一终端读取其他按键是有意义的。

在控制终端上获取文件描述符的典型方法是在/dev/tty那里打开重定向(请注意,即使进程更改了 euid,它也可以工作,因此它没有对原始设备的读取权限。这比尝试要好得多找到原始设备的路径(无论如何都无法便携地完成))。

即使 stdin 是一个 tty 设备,一些寻呼机也会喜欢lessmost打开/dev/tty(毕竟,人们可以less < /dev/ttyS0在终端模拟器中查看通过串行发送的内容)。

如果打开/dev/tty失败,那通常是因为您没有控制终端。有人可能会争辩说,这是因为您已经明确地与终端分离,因此不应该尝试进行用户交互,但是存在潜在的(不寻常的)情况,您没有控制终端设备,但您的​​ stdin/stdout 仍然是一个 tty设备并且您仍然希望进行用户交互(例如 initrd 中的紧急外壳)。

因此,如果它是终端,您可以退回以从 stdin 获取用户交互。

有人可能会争辩说,您想检查 stdout 是否是一个终端设备并且它指向与控制设备相同的终端设备(以说明man -l /dev/stdin < /dev/ttyS0 > /dev/ttyS1您不希望生成的寻呼机man执行的操作)用户交互)但这可能不值得打扰,特别是考虑到它不容易移植。这也可能会破坏其他奇怪的用例,只要 stdout 是终端设备,就可以预期寻呼机是交互式的。

  • @anatolytechtonik,打开一个文件到`/dev/tty`,它重定向到控制终端进行用户交互(当标准输入不再指向终端时)。这就是其他寻呼机所做的。如果 stdout 不是终端,您可能不想翻页,因此您也可以使用 stdout (fd 1) 读取用户输入(如果它是 tty (isatty()),但前提是 tty 设备已打开)在 stdout 上的读+写模式下,通常是这种情况,但不能保证。 (2认同)