这个打印订单有什么问题
看看这段代码:
#include #include int main() { int pipefd[2],n; char buf[100]; if(pipe(pipefd)<0) printf("Pipe error"); printf("\nRead fd:%d write fd:%d\n",pipefd[0],pipefd[1]); if(write(pipefd[1],"Hello Dude!\n",12)!=12) printf("Write error"); if((n=read(pipefd[0],buf,sizeof(buf)))<=0) printf("Read error"); write(1,buf,n); return 0; }
我希望printf在从管道中读取Hello Dude之前打印Read fd并写入fd。 但事实并非如此……请看这里 。 当我在我们的大学计算机实验室尝试相同的程序时,我的输出是
Read fd:3 write fd:4 Hello Dude!
我们的一些朋友也观察到,更改printf语句以包含更多数量的\n
字符会改变输出顺序…例如.. printf("\nRead fd:%d\n write fd:%d\n",pipefd[0],pipefd[1]);
意味着打印了读取fd然后消息Hello Dude!
然后打印写入fd。 这是什么行为? 注意:Out lab使用我们运行终端的linux服务器,但我不记得编译器版本。
这是因为printf
到标准输出流是缓冲的,但write
标准输出文件描述符则不是。
这意味着行为可以根据您的缓冲类型而改变。 在C中,如果可以确定标准输出连接到交互设备,则标准输出是行缓冲的。 否则它是完全缓冲的(参见这里有关为何如此的论文)。
行缓冲意味着它会在看到换行符时刷新到文件描述符。 完全缓冲意味着它只会在缓冲区填充时刷新(例如,4K值的数据),或者当流关闭时(或当你刷新时)。
当您以交互方式运行它时,刷新发生在write
之前,因为printf
遇到\n
并自动刷新。
但是,当你以其他方式运行它时(例如通过将输出重定向到文件或在线编译器/执行器中它可能会执行完全相同的事情来捕获数据以进行演示),刷新发生在 write
(因为printf
不是在每一行之后冲洗)。
实际上,根据以下程序,您不需要那里的所有管道内容来查看此操作:
#include #include int main (void) { printf ("Hello\n"); write (1, "Goodbye\n", 8); return 0; }
当我执行myprog ; echo === ; myprog >myprog.out ; cat myprog.out
myprog ; echo === ; myprog >myprog.out ; cat myprog.out
myprog ; echo === ; myprog >myprog.out ; cat myprog.out
,我得到:
Hello Goodbye === Goodbye Hello
你可以看到不同类型的缓冲产生的差异。
如果您想要线路缓冲而不管重定向,您可以尝试:
setvbuf (stdin, NULL, _IOLBF, BUFSIZ);
在你的程序的早期 – 它的实现定义了一个实现是否支持这个,所以它可能没有任何效果,但我没有看到很多它不起作用。
您不应该在单个文件描述符上混合使用write
和printf
。 write
更改为fwrite
。
使用FILE
函数被缓冲。 使用文件描述符的函数不是。 这就是为什么你可能会得到混合订单。
你也可以在write
之前尝试调用fflush
。
当您通过两种方式(直接IO和输出流)写入同一文件或管道或其他任何内容时,您可以获得此行为。 原因是输出流被缓冲。
使用fflush()
您可以控制该行为。
发生的事情是printf以缓冲的方式写入stdout – 字符串在输出之前保存在缓冲区中 – 而稍后的’write’写入stdout unbuffered。 这可能会导致“write”的输出首先出现,如果printf中的缓冲区稍后才刷新。
您可以使用fflush()
显式刷新 – 但更好的方法是不要将缓冲和非缓冲写入混合到同一输出。 在终端上输入man printf
, man fflush
, man fwrite
等,以了解这些命令的确切内容。