Unix系统的三种缓冲区行缓冲、全缓冲、无缓冲(以及如何调整缓冲区的类型)

来源:互联网 发布:amazon数据分析工具 编辑:程序博客网 时间:2024/06/05 08:26

from unix系统编程 p91 练习4-22

下面的程序会产生什么样的输出呢?

#include <stdio.h>

#include <unistd.h>

int main()

{

    printf("This is my output.") ;

    fork() ;

    return 0 ;

}

输出如下所示:

This is my output.This is my output.

 

(似乎很不正常!该程序中,一个printf语句输出之后,用fork函数创建子进程,然后父子进程一起返回。

怎么会输出两个printf语句呢??)

 其实,

由于缓冲的原因,printf的输出可能会写入stdout所对应的缓冲区中,而不会被写入实际的输出设备。由于这个缓冲区是用户空间的一部分,所有它会被fork复制。父进程和子进程每次终止时,从main的返回都会将缓冲区作为清理的一部分清空。

 

而下面的程序会产生什么样的输出呢?

#include <stdio.h>

#include <unistd.h>

int main()

{

    printf("This is my output.\n") ;   /*注意,原字符串后多了一个 \n */

    fork() ;

    return 0 ;

}

输出如下显示:

This is my output.

 

标准输出的缓冲通常是行缓冲,这意味着当新行包含新行的时候,缓冲区会被清空,所以会在fork之前被清空,只输出一行输入。

 

磁盘文件是完全缓冲的(fully buffered),程序调用fprintf时,实际上没有把消息写入磁盘,而是将这些字节写人如FILE结构的一个缓冲区中,可用fflush调用强制写出缓冲的任何内容。

与终端有关的文件是行缓冲(line buffered)的。

标准错误(strerror)默认情况下是不缓冲的。

附件1:

在Unix系统中,缓冲方式存在三种,分别是:

1,全缓冲

2,行缓冲

3,无缓冲

在学习APUE这本书时,程序8-1中,就很好的体现了全缓冲和行缓冲的区别,代码如下:

[cpp] view plaincopy
  1. #include<stdio.h>  
  2. #include<unistd.h>  
  3. int glob=6;  
  4. char buf[]="a write ro stdout/n";  
  5. int main()  
  6. {  
  7.         int var;  
  8.         pid_t pid;  
  9.         printf("a write to stdout/n");  
  10.         fflush(NULL);  
  11.         if((pid=fork())<0)  
  12.         {  
  13.                 printf("fork error");  
  14.         }  
  15.         else  
  16.         {  
  17.                 if(pid==0)  
  18.                 {  
  19.                         glob++;  
  20.                         var++;  
  21.                 }  
  22.                 else  
  23.                 {  
  24.                         sleep(2);  
  25.                 }  
  26.         }  
  27.         printf("pid=%d,glob=%d,var=%d/n",getpid(),glob,var);  
  28.         exit(0);  
  29. }  
 

编译成功后,我这里生成的二进制文件默认为a.out

运行:./a.out

可以看到结果如下:

[c-sharp] view plaincopy
  1. a write to stdout  
  2. pid=6587,glob=7,var=134514042  
  3. pid=6586,glob=6,var=134514041  
 

运行./a.out > temp.out

结果如下:

[c-sharp] view plaincopy
  1. a write to stdout  
  2. pid=6591,glob=7,var=134514042  
  3. a write to stdout  
  4. pid=6590,glob=6,var=134514041  

分析原因:

在./a.out输出中,标注输出是STDOUT_FILENO,是交互式的终端,所以系统采用的缓冲方式行缓冲,所以在printf函数中,输出后,立即刷新缓冲区,而在./a.out > temp.out命令中,输出流定向到了temp.out文件中,所以采用的输出方式为全缓冲方式,所以会两次输出

a write to stdout

可以将程序中fflush(NULL)加入,则只会输出一次

a write to stdout

因为flush即时刷新了缓冲区。


附件2:

使用缓冲区的目的:尽量减少使用read/write的调用
分类:
1. Fully buffered means that I/O takes place only when the buffer is fully, the process explicitly calls fflush, or the process terminates by calling exit. A common size for the standard I/O buffer is 8192 bytes; 
 
2. Line buffered means that I/O takes place when a newline is encountered, when the process calls fflush, or when the process terminates by calling exit. 
 
3. Unbuffered means that I/O take place each time a standard I/O output function is called. 
 
 
Most unix implementations of the standard I/O libarary use the following rules. 
 
1. Standard error is always unbuffered. 能够快速显示错误
 
2. Standard input and standard output are fully buffered, unless they refer to a terminal device, in which case, they are line buffered. 
 
3. All other streams are fully buffered unless they refer to a terminal device, 
in which case, they are line buffered. 

使用setbuf()和setvbuf()可以更改缓冲的类型:
setbuf(FILE *fp, char *buf)   buf用malloc()开辟,当buf为NULL时,缓冲区为不带缓冲
setvbuf(FILE *fp,char *buf, int buf_mode, int size)
        buf_mde有三种:_IOFBF 0 全缓冲, _IOLBF 1 行缓冲, _IONBF 2 不带缓冲

在任何时刻,可以使用fflush强制刷新一个数据流,此函数使该流所有未写的数据都被传递至内核。
由于fflush(stdin)的移植性不好,gcc不支持,故可以用以下代码清空stdin缓冲区:
        while ((c = getchar()) != '\n' && c != EOF);


0 0