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中,就很好的体现了全缓冲和行缓冲的区别,代码如下:
- #include<stdio.h>
- #include<unistd.h>
- int glob=6;
- char buf[]="a write ro stdout/n";
- int main()
- {
- int var;
- pid_t pid;
- printf("a write to stdout/n");
- fflush(NULL);
- if((pid=fork())<0)
- {
- printf("fork error");
- }
- else
- {
- if(pid==0)
- {
- glob++;
- var++;
- }
- else
- {
- sleep(2);
- }
- }
- printf("pid=%d,glob=%d,var=%d/n",getpid(),glob,var);
- exit(0);
- }
编译成功后,我这里生成的二进制文件默认为a.out
运行:./a.out
可以看到结果如下:
- a write to stdout
- pid=6587,glob=7,var=134514042
- pid=6586,glob=6,var=134514041
运行./a.out > temp.out
结果如下:
- a write to stdout
- pid=6591,glob=7,var=134514042
- a write to stdout
- 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);