假设ping
命令正在运行,并且在ping
仍在运行时在终端上输入内容。
现在当ping
终止并且bash
获得后退控制时,bash
将在终端上打印我在ping
运行时键入的内容。这是一个截图,显示了我的意思:
bash
如何获取此信息?我确信它没有从stdin
获取,因为当我输入"I typed this while ping was running"
时,我没有按 Enter (因此stdin
为空)。< / p>
因此,这些数据必须存储在&#34;击键缓冲区&#34;中,并且bash
从此缓冲区中读取。
我的问题是,bash
如何从此缓冲区中读取(它调用的函数是什么......)?
答案 0 :(得分:1)
这是关于readline
library的问题(这里是关于它的more approachable page)。
你可以通过Python看到这一点,它是在大多数发行版上使用readline支持编译的:
>>> import time
>>> time.sleep(5)
I am typing this during the sleep>>> I am typing this during the sleep
然而,我也碰巧有一个没有readline支持:
>>> import time
>>> time.sleep(5)
I am typing this during the sleep>>>
(这也可以通过cat | python -i
实现,因为cat
不使用readline,而python禁用readline,因为它的输入不是终端。)
我猜测会发生什么:
答案 1 :(得分:0)
这就是我认为发生的事情:
bash
负责回显它收到的所有内容)。bash
执行ping
之前,bash
重新启用tty设备线缓冲和tty设备回显。然后bash
执行ping
。"I typed this while ping was running"
,而ping
正在显示其输出。 tty设备会将此字符串回显给终端窗口,但也此字符串现在缓存在tty设备中。ping
终止,bash获得控制权。bash
,bash
将在终端窗口上显示它。答案 2 :(得分:0)
没有特殊的&#34;读取终端队列&#34; bash中正在进行的机制。它只是普通的read
和write
系统调用。
在Linux上,&#34; tty device&#34;总是被缓冲。如果键入的速度比接收程序读取的速度快,则字符不会掉入位桶;它们被放置在终端设备的输入队列中,可以通过read
系统调用从中检索它们。
read
调用的原型为ssize_t read(int fd, void *buf, size_t count);
。当终端驱动程序决定应返回read
调用时,它会从终端的输入队列中删除count
个字节,并将它们放入buf
。如果队列不包含count
个字节,则读取队列中的所有字节;如果它包含超过count
个字节,则剩余的字节将保留在下一个read
的队列中。
count
对终端本身的运作没有影响;当一个字节可用时,read(0, buf, 1)
不会导致read
返回。它只限制放入buf
的字节数。
有。但是,有许多控制设置会影响read
调用的处理方式。在这种情况下,最重要的是ICANON
标志。如果设置了此标志(&#34;规范模式&#34;),则在键入NL字符之前,不会唤醒等待tty上的read
系统调用的进程。 (实际上,有四个字符会唤醒进程:NL,EOL,EOL2和EOF。)在规范模式下,内核驱动程序还处理一些行编辑字符,例如ERASE字符。 (所有这些字符都可以通过termios
配置,这样当我说&#34; NL字符&#34;时,我的意思是&#34;当前配置为NL的字符。默认情况下,NL字符是& #34;输入&#34;键,EOF是control-D。)
如果未设置ICANON,则终端处于非规范模式,并应用VMIN和VTIME设置。 VMIN是进程被唤醒之前必须存在的最小字符数; VTIME是终端驱动程序在放弃和唤醒进程之前等待输入的最短时间。如果同时设置了VMIN和VTIME,则内核驱动程序将等待(无限期)第一个字符,然后等待每个连续字符的VTIME,直到读取VMIN字符。如果既未设置VMIN也未设置VTIME,则read
调用将始终立即返回。
独立于规范模式设置,您还可以配置终端的回显行为。在最简单的配置中,您可以设置ECHO。如果设置了ECHO,则在键入时,终端驱动程序将回显可打印字符。如果未设置ECHO,则终端驱动程序不会回显字符。默认情况下,ECHO已设置。
Bash通常使用readline库来处理终端输入。你可以告诉bash不要使用readline,在这种情况下行为会有所不同;我只是用readline描述正常情况。
当bash处于活动状态且控制终端时,readline将终端置于非规范模式并关闭回显。它将VMIN设置为1,因此只要输入单个字符,read
调用就会返回,然后进入类似下面的循环:
while (1) {
ssize_t nread = read(0, buf, 1);
if (nread && isprint(buf[0]))
write(1, buf, 1); /* Echo the character */
else {
/* Lots of complicated logic to handle other characters */
}
}
在bash让子进程执行之前,它会通过将终端重新置于规范模式并启用echo来重置终端。除非正在执行的命令改变终端模式,否则它会一直保持,直到命令退出,此时bash重新获得控制权,转换规范模式并回显,然后返回readline循环。
假设正在执行的命令不会更改终端设置,并执行一段时间(如问题中的ping
示例)。当ping
正在执行时,终端处于规范模式,回显打开,没有人从终端设备读取。因此,您输入的任何内容都将被放入终端队列并进行回显。 &#34;任何&#34;包括通常会终止read
的内容,例如Enter键。这是因为没有read
终止。
当ping
最终完成时,bash将终端更改回非规范模式并关闭回显,以便您现在键入的字符不会被终端驱动程序回显。然后调用readline启动上面的循环。但是,此时终端队列中存在大量内容,远远超过VMIN设置所需的一个字符。因此read
调用会立即返回一个字符,然后该循环中的write
调用会回显刚刚读取的字符。当然,这个角色已经被终端驱动程序所回应,但这个事实并没有神奇地记录在角色的二进制编码中;它只是一个普通的角色,所以它第二次得到回应。这种情况一直持续到终端队列被清空或者您之前键入的某个字符需要重新注意。如果终端队列只包含一些普通的可打印字符,它们将全部回显,下一个read
调用将阻止,直到您输入内容为止。