首先介绍一下UNIX里面关于标准IO的几种缓冲机制
全缓冲 :全缓冲指的是系统在填满标准IO缓冲区之后才进行实际的IO操作;注意,对于驻留在磁盘上的文件来说通常是由标准IO库实施全缓冲。
行缓冲 :在这种情况下,标准IO在输入和输出中遇到换行符时执行IO操作;注意,当流涉及终端的时候,通常使用的是行缓冲。
无缓冲 :无缓冲指的是标准IO库不对字符进行缓冲存储;注意,标准出错流stderr通常是无缓冲的。
通过系统调用和库函数认识IO缓冲
先看一段代码:
#include <stdio.h>
#include <string.h>
int main()
{
const char *msg0 = "hello printf\n";
const char *msg1 = "hello fwrite\n";
const char *msg2 = "hello write\n";
printf("%s", msg0);
fwrite(msg1, strlen(msg0), 1, stdout);
write(1, msg2, strlen(msg2));
fork();
return 0;
}
【运行结果】:
hello printf
hello fwrite
hello write
对进程实现重定向:./test > file,
【运行结果】:
hello write
hello printf
hello fwrite
hello printf
hello fwrite
printf和fwrite(库函数)都输出了2次,而write只输出了一次(系统调用)。这是因为fork。
- 一般C库函数写入文件是全缓冲的,而写入显示器是行缓冲。
- printf和fwrite库函数会自带缓冲区,当发生重定向到普通文件时,数据的缓冲方式由行缓冲变成了全缓冲。
- 而我们放在缓冲区中的数据,不会立即被刷新,甚至fork之后,但是进程退出之后,会被统一刷新,写入文件当中。
- fork的时候,父子数据会发生写时拷贝,所以当父进程准备刷新的时候,子进程也就有了同样的一份数据,随既产生两份数据。
- write没有变化,说明没有所谓的缓冲
因此,缓冲取是库函数在系统调用的“上层”,是对系统调用的“封装”。
再来看几个退出函数
exit ():调用exit函数之后,它首先会执行一系列的清理处理,包括调用执行各终止处理程序,关闭所有标准IO流等,然后进入内核。
_exit ():与exit不同的是,它不进行清理工作而直接进入内核。此函数由POSIX.1说明,放在unistd.h里面。
我们通过代码来看一下
#include <stdio.h>
int main()
{
printf("hello world!");
_exit(0);
}
本来想打印hello world,但是终端没有显示。
#include <stdio.h>
int main()
{
printf("hello world!");
exit(0);
}
【运行结果】:
hello world!
由于printf函数是行缓冲的(因为它要往终端输出数据),而且要打印的字符串不带换行符,因此在它没有遇到换行符或者没有填满缓冲区之前不会进行实际的IO操作,而在_exit函数有立即进入内核没有处理IO缓冲区,所以我们在终端上看不到hello world语句。当我们调用exit()函数时,在终端看到了hello world