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

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

我说得对吗,所有从键盘输入的输入都会经过一个控制终端?这意味着如果程序在没有控制终端的情况下运行,它将无法接收任何用户输入。这适用于 Linux 中的每种程序吗?

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

$ ./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')

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

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

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

答案1

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

该设备不一定是控制终端流程(或任何与此相关的流程)。

只要您拥有该设备文件的读取权限,您就可以执行此cat /dev/pts/x操作,并且可以读取另一端终端(如果有)上键入的内容。

实际上,如果它是进程的控制终端,并且该进程不在该终端的前台进程组中,则该进程在尝试从中读取数据时通常会被挂起(如果它在前台进程组中,则它会被挂起)。如果您发送^C//无论进程是否正在从终端设备读取数据,^Z都会收到 SIGINT/SIGTSTP/SIGQUIT )。^\如果终端设备不是终端设备,这些事情就不会发生控制终端进程的名称(如果该进程是不同会话的一部分)。就是这样控制终端是关于。这是为了作业控制由交互式 shell 实现的机制。除了 SIGTTIN/SIGTTOU 和 SIGINT/SIGTSTP/SIGQUIT 信号之外,控制终端还参与在终端挂起 hup 时发送 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,则从运行于 的 shell 中连接到 X 服务器以进行用户 I/O 的应用程序将xterm继承firefox控制终端来自其外壳父级。^C如果 shell 在前台启动它,则在终端中会杀死它。它还将在该文件上打开其文件描述符 0、1 和 2(stdin、stdout 和 stderr)/dev/pts/<x>(同样是从其 shell 父级继承的)。并且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甚至某些实现)会继续从 stdin 读取输入,因此它们可以编写脚本。vi

寻呼机是一种典型的应用程序,即使用户的输入不是终端(至少当其输出仍然到达终端时),它仍然需要与用户交互。所以他们需要另一个渠道终端设备接受用户输入。问题是:他们应该使用哪种终端设备?

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

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

即使 stdin 是 tty 设备,某些寻呼机也会喜欢lessmost打开/dev/tty(毕竟,可以less < /dev/ttyS0从终端仿真器内查看通过串行发送的内容)。

如果打开/dev/tty失败,通常是因为您没有控制终端。有人可能会说这是因为你已经明确地超然的从终端,所以不应该尝试进行用户交互,但有潜在的(不寻常的)情况,你没有控制终端设备,但你的标准输入/标准输出仍然是一个 tty 设备,你仍然想要进行用户交互(就像 initrd 中的紧急 shell)。

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

有人可能会说你想检查 stdout 是否是终端设备它指向与控制设备相同的终端设备(以考虑所做的事情,man -l /dev/stdin < /dev/ttyS0 > /dev/ttyS1例如您不希望生成寻呼机man进行用户交互),但这可能不值得打扰,特别是考虑到它并不容易便携地做。这也可能会破坏其他奇怪的用例,只要标准输出是终端设备,寻呼机就可以交互。

相关内容