假设我有一个C程序,它有下面的循环。
while ((c = getchar()) != EOF) {
...
}
这个循环看起来像是在tty上一个接一个地读取字符。但是一旦输入了字符,我就不应该再改变字符了,很明显,这不会发生。
这就意味着tty必须保留一些缓冲区来保存它已经接收到的值,这些值还没有被推送到stdin。
这样做对吗?如果是这样的话,我怎样才能获得这个缓冲区中的字符,而不需要将它们转到stdin?我试着使用ioctl和FIONREAD,但似乎不起作用(缓冲区的大小总是零,即使终端中有字符),而且由于这些数据不在stdin中,这意味着从stdin读取的方法将不起作用(这些是对类似问题的回答)
2条答案
按热度按时间bzzcjhmw1#
简短答案:
termios.h
是一个POSIX标准的头文件,允许您将tty设置为raw模式,而它通常是 *cooked模式 *。建议阅读其文档/手册页。
here链接了一个网页,该网页 * 逐步 * 介绍如何使用
termios.h
设置原始模式。说明:
默认情况下,终端的I/O是行缓冲的,也就是说,一旦一行被终止,输入就保证被刷新/发送到你的程序。
在输出中,这是通过
\n
或fflush(stdout);
实现的,而在输入中,这是通过用户按下[ENTER]
键实现的。让终端进入 raw 模式允许它是这样的,在一个键被按下,输入信号被发送到您的程序,沿着许多其他默认功能,如回声被禁用。
对于任何稍微复杂的CLI程序,尤其是像vim、htop等程序,这是非常常见的做法。
ncgqoxb02#
如上所述,当采用低级方法进行用户输入编程时,丢失重要抽象层的窘境是非常真实的。另一种避免重新实现readline等功能的方法是直接从控制tty读取字节到程序中,根据需要,可以是原始的,也可以是由termios. h处理的,然后根据需要在伪终端或管道上产生子进程来进一步处理这些字节。
例如,我将在C语言中运行一个父进程,它又派生出运行bash、perl、python、GNU readline等的子进程。顺便提一下,像bash这样的shell子进程需要一个伪终端来运行,以便与作业控制完全交互,而像perl这就把我们带回到你最初的问题,答案是文件描述符0,你的stdin,最终被添加到struct epoll_event中,就像你的子进程的输出描述符一样,用于通过epoll_wait进行监视()。您的主进程变成了一个高容量的进程间字节流路由器。键盘输入变成了您正在处理的另一个流。例如,使用这种技术,你可以无缝地合并internet和其他类型的套接字。它们本质上都是由unix下的文件描述符控制的文件。
如果您使用的是物理连接的输入设备(如鼠标、触摸板、游戏杆等),那么了解一下Linux输入子系统用户空间API可能也会有所帮助。