C标准I/O缓冲区:全缓冲和行缓冲

ISO C标准I/O提供了全缓冲和行缓冲

全缓冲:在进行I/O操作时,只有当I/O缓冲区被填满时,才进行真正的I/O操作。所以对于全缓冲的缓冲区可由标准I/O例程自动刷新,即当缓冲区填满时,还有一种方法就是调用函数fflush进行刷新。

行缓冲:在I/O操作时,输入输出遇到换行符时进行,进行真正的I/O操作。对于行缓冲,标准I/O每一行缓冲区的长度是固定的,所以只要填满了缓冲区,即使没有遇到换行符,也换刷新缓冲区。

当然标准I/O还提供了不带缓冲的类型,就是不对字符进行缓冲操作。


那么全缓冲和行缓冲都用在I/O操作的哪些地方呢。

ISO C要求:
•当且仅当标准输入和输出不涉及交互式设备(终端设备)时,它们才是全缓冲。
•标准出错不是全缓冲。

但是这并没有告诉我们当涉及到交互式设备时,标准输入输出是行缓冲还是不带缓冲,以及标准出错时行缓冲还是不带缓冲。很多系统(FreeBSD,Linux,Mac OS,Solaris)默认使用下面类型缓冲:
•如果标准输入输出涉及终端设备,则它们是行缓冲,否则是全缓冲。
•标准出错不带缓冲。

我们都知道shell为每个进程都定义了三个文件描述符:0,1,2。这三个文件描述符分别与进程的标准输入,标准输出和标准出错输出相关联。在unistd.h头文件中这三个常量分别替换成STDIN_FILENO,STDOUT_FILENO,STDERR_FILENO符号。在ISO C中分别对应与stdin,stdout,stderr。


由apue chapter 8中的一个例子可以深刻的体会到上面所说的全缓冲和行缓冲的区别;




[cpp] view plaincopyprint?
01.#include <stdio.h>  
02.#include <string.h>  
03.  
04.#include <unistd.h>  
05.  
06.int glob = 6;  
07.char buf[] = "anonymalias\n";  
08.  
09.int main()  
10.{  
11.         int var;  
12.         pid_t pid;  
13.  
14.         var = 8;  
15.  
16.         if(write(STDOUT_FILENO, buf, strlen(buf)) != strlen(buf))  
17.         {  
18.                 fprintf(stderr, "write error");  
19.                 return 0;  
20.         }  
21.  
22.         printf("before fork()...\n");  
23.  
24.         if((pid = fork()) == -1)  
25.         {  
26.                 fprintf(stderr, "fork error");  
27.                 return 0;  
28.          }  
29.          if(pid == 0)  
30.         {  
31.                 glob++;  
32.                 var++;  
33.          }  
34.         else  
35.         {  
36.                 sleep(2);  
37.         }  
38.  
39.         printf("parent process id:%d ", getppid());  
40.         printf("process id:%d, glob:%d, var:%d\n", getpid(), glob, var);  
41.   
42.  
43.         return 0;  
44.}  


若执行输入为:./unix8_1时,输出结果为:
anonymalias
 before fork()...
 parent process id:7222 process id:7223, glob:7, var:9

parent process id:5200 process id:7222, glob:6, var:8

若执行输入为:./unix8_1 > temp 时,查看temp文件,输出结果为:

anonymalias
 before fork()...
 parent process id:7237 process id:7238, glob:7, var:9
 before fork()...
 parent process id:5200 process id:7237, glob:6, var:8


之所以会出那先上面的结果是因为:在执行unix8_1时,默认的标准输出为终端设备即显示器。这时标准输出缓冲区为行缓冲,在执行到第22行输出后缓冲区会被刷新。

而在./unix8_1 > temp时标准输出被重定向到文件,非交互式设备。这时标准输出缓冲区为全缓冲,在执行到第22行时,不会进行输出,缓冲区不会被刷新。只有等到缓冲区满或进行fflush,才会进行输出。

而在fork生成子进程后,子进程会复制父进程的数据空间,当然包括父进程打开的文件描述符所对应的缓冲区。在./unix8_1时中第22行输出后缓冲区已经被刷新即清除,所以子进程不会复制这部分缓冲区;而在执行./unix8_1 > temp时,第22行标准输出的缓冲区是全缓冲,不会被刷新,所以子进程会复制这部分缓冲区,在程序结束时才会对输出缓冲区进行刷新。所以最后会输出两次“before fork()...”,因为父进程和子进程都有自己的这部分缓冲区。


要想在执行./unix8_1 和./unix8_1 > temp 时输出结果一样,可以再第22行后面加上一句fflush:




[cpp] view plaincopyprint?
01.22         printf("before fork()...\n");  
02.23         fflush(stdout);  

这样输出的结果就都是 
anonymalias
 before fork()...
 parent process id:7222 process id:7223, glob:7, var:9 


parent process id:5200 process id:7222, glob:6, var:8

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值