C Linux标准数据缓冲

C Linux标准数据缓冲,c,linux,C,Linux,在下面的程序和示例运行中,我希望看到“stdin包含9个字节”,但正如您在示例运行中看到的,我得到了“stdin包含0个字节”。为什么呢?如何修复此程序以获取stdin中的实际未读字节数 节目: #include <stdio.h> #include <sys/ioctl.h> #include <unistd.h> void main() { if ( (setvbuf(stdin, NULL, _IONBF, 0) | s

在下面的程序和示例运行中,我希望看到“stdin包含9个字节”,但正如您在示例运行中看到的,我得到了“stdin包含0个字节”。为什么呢?如何修复此程序以获取stdin中的实际未读字节数

节目:

#include <stdio.h>
#include <sys/ioctl.h>
#include <unistd.h>

void main() {
    if ( (setvbuf(stdin, NULL, _IONBF, 0) |
            setvbuf(stdout, NULL, _IONBF, 0)) != 0) {

        printf("setting stdin/stdout to unbuffered failed");
        return;
    }

    printf("type some keys\n");
    sleep(3);
    printf("\n");

    unsigned long bytesUnread=0;
    if (ioctl(0, FIONREAD, &bytesUnread) != 0) {
        printf("ioctl error");
        return;
    }
    printf("stdin contains %ld bytes\n", bytesUnread);
}
这里是另一个运行示例,我在其中按enter键,您可以看到它按预期工作

$ ./a.out                 
type some keys
asd

stdin contains 4 bytes

setvbuf
设置文件流的输出缓冲。您不能更改输入缓冲,因为这样做毫无意义——在实际读取底层文件描述符之前,您通常不知道可以从底层文件描述符读取什么。即使在您可以知道的情况下(例如,通过使用ioctl进行检查),由于以后的事件以及访问文件描述符的其他线程和进程,这种情况也可能会改变。所以,在你阅读之前,你永远无法确切地知道你要读什么

看看您的示例运行,看起来您使用的是终端,而不是按enter键?在这种情况下,输入将保存在终端缓冲区中(以防您稍后点击backspace),而ioctl调用(检查文件描述符的缓冲区)将看不到它


在任何情况下,获取输入缓冲区中的数据量是没有用的,因为在实际读取之前,尝试用这些知识做任何事情都是一个竞争条件——您无法知道是否有其他进程或驱动程序正在修改缓冲区。因此,对于任何实际使用,您只需要读取输入数据,并根据(原子)读取系统调用返回的结果作出反应。

从文件中重定向stdin,或在按下几键后按enter键,您将看到您的代码按预期工作,与您是否调用
setvbuf
无关,因为您的问题不是被缓冲的
文件
流。
文件
流甚至不涉及您的
ioctl
。相反,您的问题是字节尚未传输。它们位于内核的行编辑缓冲区中,用于规范模式tty,允许您在发送它们之前退格、CtrlW等


如果您希望tty层在终端上生成字节时传输字节,而不是仅在行编辑后进行聚合,则需要将tty从规范模式中取出。
termios.h
接口是如何实现这一点的(请参见
man 3 termios
)。通常最简单的方法是
tcgetattr
cfmakeraw
,然后
tcsetattr
,但是,
cfmakeraw
不是完全可移植的,因此最好自己做相应的更改。

如何从终端缓冲区而不是文件描述符缓冲区获取字节数?我希望TIOCINQ可以做到,但这似乎只是linux上FIONREAD的别名,看来没有办法了。在任何情况下,it或FIONREAD对好奇心以外的任何东西都没有任何用处,因为在你真正尝试之前,你不知道你能读多少,这个答案是错误的
setvbuf
绝对会设置输入缓冲。如果将其设置为无缓冲并继续调用
fgetc
strace
将为每个字节显示一个单独的
read
。我不知道您所问问题的答案,但我怀疑解决您更大问题的方法是使用或使用库。当显示
字节未读
为type
int
时,为什么要使用
unsigned long
?此外,除非您在没有操作系统的独立嵌入式系统上工作,否则您对
main()
的声明不正确,请参阅:。另见:
$ ./a.out                 
type some keys
asd

stdin contains 4 bytes