这个打印订单有什么问题

看看这段代码:

#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); 

在你的程序的早期 – 它的实现定义了一个实现是否支持这个,所以它可能没有任何效果,但我没有看到很多它不起作用。

您不应该在单个文件描述符上混合使用writeprintfwrite更改为fwrite

使用FILE函数被缓冲。 使用文件描述符的函数不是。 这就是为什么你可能会得到混合订单。

你也可以在write之前尝试调用fflush

当您通过两种方式(直接IO和输出流)写入同一文件或管道或其他任何内容时,您可以获得此行为。 原因是输出流被缓冲。

使用fflush()您可以控制该行为。

发生的事情是printf以缓冲的方式写入stdout – 字符串在输出之前保存在缓冲区中 – 而稍后的’write’写入stdout unbuffered。 这可能会导致“write”的输出首先出现,如果printf中的缓冲区稍后才刷新。

您可以使用fflush()显式刷新 – 但更好的方法是不要将缓冲和非缓冲写入混合到同一输出。 在终端上输入man printfman fflushman fwrite等,以了解这些命令的确切内容。