【问题标题】:When reading from /dev/tty, what is happening in input and keyboard buffer?从 /dev/tty 读取时,输入和键盘缓冲区中发生了什么?
【发布时间】:2022-01-18 10:19:02
【问题描述】:

我在玩下面两个代码sn-ps

// Snippet1 in C
#include <stdio.h>

int main(){
    FILE * fp = fopen("/dev/tty", "r");

    int c = getc(fp);
    printf("%d", c);
}
// Snippet2 in Rust
use std::io::Read;
use std::fs;

fn main() {
    let mut f: fs::File = fs::File::open("/dev/tty").unwrap();
    let mut buf: [u8;1] = [0];

    f.read_exact(&mut buf).unwrap();
    print!("byte: {}", buf[0]);
}

上面的代码要做的是从用户键盘读取一个字节,然后将它打印到标准输出。 令人困惑的是两个 sn-ps 有不同的行为:

➜  playground gcc main.c -o main
➜  playground ./main
a                                  # input a and press Enter
97%
➜  playground cargo run -q
a                                  # input a and press Enter
byte: 97%                                                                ➜  playground
➜  playground

不好意思上面代码的格式,不知道怎么把提示放在换行的开头:(

请注意,Rust代码执行后有两个 shell提示➜ playground

我猜Enter 被发送到输入缓冲区,就好像我在执行后按下它一样。

如果我知道缓冲区中实际发生了什么,我会找出这种区别的原因,所以我想知道那里发生了什么?

顺便说一句,我的环境:

➜  playground rustc --version
rustc 1.57.0 (f1edd0429 2021-11-29)
➜  playground gcc --version
gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

如果我的问题不被允许,请随时要求我删除它:) 在此先感谢:)

【问题讨论】:

  • 好吧,read_exact 显然没有读取换行符,因为它不适合缓冲区。我猜getc只是默默吞下它?
  • 换行符实际上是写入 /dev/tty 的。因此,如果再次调用 getc,它应该读取换行符。
  • @Cerberus 感谢您的评论!有两种读取流,一种直接从键盘读取(char a),另一种从标准输入(Enter keystroke)读取。而在我看来,getc 属于前者,因为它只从它的参数(文件 /dev/tty)中读取,也许它是吞下输入的其他东西?
  • @kiner_shah,谢谢您的评论!你能进一步解释为什么Newline is written to /dev/tty actually吗?我的想法是当我输入“a”时,getc 完成了它的工作,并且换行符被写入输入缓冲区。
  • @SteveLau,我运行了以下代码,您可以看到 10 是输出(换行符的 ASCII),只要您打印输入。 onlinegdb.com/AUkca3lGy

标签: c linux rust buffer input-buffer


【解决方案1】:

请注意,在 C 中,FILE* 和相应的函数是缓冲的,因此 C 程序读取键和换行符并将它们放入缓冲区,而您的 Rust 代码不使用缓冲。因此,当您的 Rust 程序完成时,换行符仍在 TTY 的内核缓冲区中,因此您的 shell 可以看到换行符,而您的 C 程序已从内核缓冲区中删除了换行符。

使用这个无缓冲的 C 代码,您应该获得与 Rust 程序相同的行为:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>

int main() {
    int fd = open ("/dev/tty", O_RDONLY);

    char c;
    read (fd, &c, 1);
    printf("%d", c);
}

或使用此缓冲 Rust 代码与您的 C 程序具有相同的行为:

use std::io::{ BufReader, Read };
use std::fs;

fn main() {
    let mut f = BufReader::new (fs::File::open("/dev/tty").unwrap());
    let mut buf: [u8;1] = [0];

    f.read_exact(&mut buf).unwrap();
    print!("byte: {}", buf[0]);
}

【讨论】:

  • 所以基本上,在 Rust 中,当按下 enter 时会发生另一个回显,导致按下 enter 两次的行为对吗?但是在 C 中,回显不会发生,因为输入在缓冲区中并且未被处理。
  • @kiner_shah 不,只有一个回显,但在无缓冲模式下(如 OP Rust),“enter”不会被程序捕获,因此对于 shell 仍然可用,而在缓冲模式下模式(如 OP C)“进入”被程序捕获并丢弃,因此对 shell 不再可见。
  • @kiner_shah,Snippet2 没有缓冲,read_exact 只接受 char 'a',当按下Enter 时,会留下换行符并发送到输入缓冲区,并且 shell 从输入缓冲区中读取,使这里有两个提示。 Snippet1 是缓冲的,所以fgetc 将 'a' 和换行符都带入它的缓冲区,因此输入缓冲区是空的,我认为:)
  • 我认为我对这个缓冲概念的理解是不正确的,需要修改它:-) 谢谢大家。
猜你喜欢
  • 1970-01-01
  • 2011-07-16
  • 2018-03-05
  • 1970-01-01
  • 2012-02-10
  • 2019-06-30
  • 1970-01-01
  • 2012-06-03
  • 1970-01-01
相关资源
最近更新 更多