【发布时间】:2022-01-13 18:00:19
【问题描述】:
我正在尝试禁用程序输入/输出的缓冲,以便尽快传输和接收字符,但根据我在 stdio 和termios 文档。这是一个小例子来解释我的意思:
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
int trapState;
void sigInt(int blah) {
trapState++;
if (trapState == 5) exit(EXIT_FAILURE);
}
int main(int argc, char * argv[]) {
int b = 0;
trapState = 0;
signal(SIGINT, &sigInt);
while (1) {
getchar();
if (trapState > b) puts("boop\n");
b = trapState;
}
}
直截了当:当你点击 ^C 时,它应该打印一个字符串,然后第五次保释,对吧?
然而,奇怪的是,除非您按 ENTER,否则输出不会显示在终端上;此外,如果您在按 ENTER 之间多次按 ^C,只会打印一条消息, 这意味着 getchar() 一直阻塞到行尾,并且 SIGINT 处理程序正在递增 b 每次迭代不止一次,即使getchar() 在没有输入的情况下应该返回 EOF。 SIGINT 处理程序在它应该运行的时候运行,因为它会在第五次左右立即转储,但程序一直在等待getchar(),直到行缓冲区被填满。
所以,好吧,让我们 A. 尝试解决 stdio 和 B. 上的缓冲问题。考虑多个中断,看看会发生什么:
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
int trapState;
void sigInt(int blah) {
trapState++;
if (trapState == 5) exit(EXIT_FAILURE);
}
int main(int argc, char * argv[]) {
int b = 0;
trapState = 0;
setbuf(stdout, NULL);
setbuf(stdin, NULL);
signal(SIGINT, &sigInt);
while (1) {
while (trapState > b) {
puts("boop\n");
b++;
}
fflush(stdout);
getchar();
}
}
现在会打印多条消息,但仍仅在按下 ENTER 后才打印,这意味着 getchar() 仍处于阻塞状态,尽管在 stdin 上禁用了缓冲。 (并且我们无法在调用 getchar() 之前检查 EOF,因为 feof() 只能保证在 last 操作之后反映流的状态。)
但 TTY 驱动程序也默认设置了行缓冲区。是不是挂在里面了?让我们找出答案:
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <termios.h>
int trapState;
struct termios t, z;
void sigInt(int blah) {
trapState++;
if (trapState == 5) exit(EXIT_FAILURE);
}
void dump() {
tcsetattr(fileno(stdin), TCSANOW, &z);
}
int main(int argc, char * argv[]) {
int b = 0;
trapState = 0;
setbuf(stdout, NULL);
setbuf(stdin, NULL);
tcgetattr(fileno(stdin), &z);
tcgetattr(fileno(stdin), &t);
t.c_lflag &= ~(ICANON);
t.c_cc[VMIN] = 0;
t.c_cc[VTIME] = 0;
tcsetattr(fileno(stdin), TCSAFLUSH, &t);
fflush(stdout);
signal(SIGINT, &sigInt);
atexit(&dump);
while (1) {
while (trapState > b) {
puts("boop\n");
b++;
}
fflush(stdout);
getchar();
tcflush(fileno(stdin), TCIOFLUSH);
}
}
相同的结果。我有一个 heck 时间来解决这个问题 - getchar() 曾经 从标准输入返回 EOF 吗?或者它只是阻塞直到读取一个字符,无限期地?这种行为似乎是标准的——我在 Linux 系统和 BSD 系统上得到了相同的结果——但根据我正在阅读的文档所说的,它的行为方式并不像我期望的那样。我在这里错过了什么?
【问题讨论】:
-
您正在阅读的文档是什么?
-
@MC68020 嗯,谢谢;但我还是很好奇。对此的评论者说,不可能通过标准输入来做到这一点,并且“解决方案”代码使用 read() 代替 - 但这不是 stdlib 在后端使用的吗? does getchar() 曾经在标准输入上返回 EOF 吗?
-
getchar()如果您点击Ctrl-D并且没有未读字符,则返回EOF。否则,第一个 Ctrl-D 返回未读字符,第二个 Ctrl-D 返回 EOF。终端驱动程序允许行编辑(与 BackSpace 一样),因此它不能立即发送字符(BackSpace 不能将它们从进程中吸回)。您必须更改终端模式才能在字符可用时立即获取字符,而 stdio 不支持。 -
getchar被缓冲,你可以用setvbuf改变它;否则你所能做的就是中断对 block 数据的读取。