从 /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]);
}
我正在玩以下两个代码片段
// 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]);
}