从 /dev/tty 读取时,输入和键盘缓冲区发生了什么?

When reading from /dev/tty, what is happening in input and keyboard buffer?

我正在玩以下两个代码片段

// 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]);
}

上面的代码要做的是从用户键盘读取一个字节,然后打印到stdout。 令人困惑的是两个片段有不同的行为:

➜  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

I am sorry about the format of the above code, I don't know how to place the prompt at the start of a new line:(

请注意,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.

如果我的问题不被允许,请随时让我删除它:) 提前致谢:)

请注意,在 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]);
}