Ctrl-Z 有时需要 Enter 才能返回到 shell 提示符

Ctrl-Z 有时需要 Enter 才能返回到 shell 提示符

我已登录到终端(或终端仿真器),阅读手册页或读取文件less等。我使用Ctrl-Z来暂停前台应用程序,返回到(bash)shell,执行某些操作,返回全屏应用程序,再次离开等。

最终,但通常不是在第一次迭代期间,Ctrl-Z不会将我带到命令提示符,而是保持中间状态,其中键盘输入显示在终端上,readline 编辑不可用,退格删除确实有效,并且在按下 时将键入的输入提供给 shell Enter

有人能解释一下为什么会发生这种情况以及如何禁用它吗?虽然我已经遇到这种现象好几年了,但我在互联网上没有找到任何关于这种现象的参考资料。

答案1

我可以解释,但我不知道如何修复它。这个问题也困扰了我好几年。至少 20 年了。它不是某个特定程序中的一个错误,而是 3 个程序之间的竞争条件,这些程序单独按照设计运行,而且它非常普遍,适用于许多程序组合。

当您^Z在阅读手册页时键入内容时,您会将 发送SIGTSTP到前台进程组,该组由man进程及其后代组成。寻呼机 ( less) 就是这些后代之一。

man被立即暂停,但less实际上并没有。它有一个信号处理程序。这是有原因的。它改变了 tty 模式(因此它可以在输入字符后立即读取字符),并且它希望在放弃控制之前恢复原始模式并整理屏幕。所以它这样做了。

与此同时(这是一个很不雅的词),shell 注意到进程man已经停止。而糟糕的是:shell 根本不知道进程的less存在。Unix 进程通常无法知道它们的孙子进程。因此从 shell 的角度来看,工作已经停止,尽管实际上less整理工作还没有完成。

根据不完整的信息,shell 将自己放回前台,打印作业通知消息,为自己的行编辑器设置 tty 模式,并打印提示。但它正在与 竞争less,后者仍在尝试整理。当 shell 打印提示并设置 atty 模式时,less 正忙于移动光标并清除屏幕的底行(提示所在的位置less)。

因为这是一场比赛,所以可能有很多种结果,取决于操作的顺序,但最烦人的是这个:

  1. shell 打印作业通知,为其行编辑器设置 tty 模式,并打印提示。
  2. less将光标移到左下角,清除底线(删除less提示shell 提示符),并恢复通用默认 tty 模式(“cooked”模式)。
  3. shell 将自身置于前台并开始接受输入。

现在你正盯着一个空白行,前面有一个作业通知,而 shell 正试图读取按键,但不知道 tty 已经设置回 cookup 模式,因此在输入完整行之前不会向 shell 发送任何输入。

您实际上可以在这里输入命令,甚至编辑它,如果您足够聪明,能够弄清楚如何让您的输入通过 tty 的行编辑器和 shell 的行编辑器,并且即时反馈仅来自 tty 的行编辑器。

这是一个混乱的情况,修复它似乎需要进程之间的协调,超出了内核目前允许的范围。shell 需要一种方法来查询整个进程组的挂起状态,以及一种方法来区分孙进程,比如less那些打算SIGTSTP在不久后自行挂起的句柄,以及守护进程类孙进程,这些孙进程只是忽略了其他SIGTSTP任务,打算在后台继续运行,而其他任务则被挂起。

相关内容