缓存区对比分析
1.测试缓存区大小
全缓存:4K,测试程序输出为4096,1K = 1024;
#include <stdio.h>
#include <stdlib.h>
int main(int argc, const char *argv[])
{
FILE *fp = NULL;
if( (fp = fopen("test", "w")) == NULL)
{
perror("fopen error");
exit(1);
}
fputc('a', fp);
printf("全缓存大小: %d\n", fp->_IO_buf_end - fp->_IO_buf_base);
return 0;
}
行缓存:stdin和stdout均为1K,测试程序输出为1024,1K = 1024;stderr为0;
#include <stdio.h>
#include <stdlib.h>
int main(int argc, const char *argv[])
{
FILE *fp = NULL;
if( (fp = fopen("test", "w")) == NULL)
{
perror("fopen error");
exit(1);
}
fputc('a', fp);
printf("标准输入大小: %d\n", stdin->_IO_buf_end - stdin->_IO_buf_base);
printf("标准输出大小: %d\n", stdout->_IO_buf_end - stdout->_IO_buf_base);
printf("标准出错大小: %d\n", stderr->_IO_buf_end - stderr->_IO_buf_base);
return 0;
}
2.测试缓存区刷新条件
缓存区写满
先来看一个没写满的例子:
#include <stdio.h>
int main(int argc, const char *argv[])
{
printf("a");
while(1)
;
return 0;
}
上面这个程序编译运行后,将不会输出 a 这个字符,因为标准输出的大小为1024个字符,明显没有写满,所以不会有字符输出;
好,然后我们来把它写满,看看它会不会输出
咱们利用printf函数往标准输出缓存区里写入1025个字符,看看现象。按写满缓存区的刷新条件来看,终端会输出1024个字符后,然后缓存区里还是1个 a 字符,所以还会在while处进行死循环;
#include <stdio.h>
int main(int argc, const char *argv[])
{
for(int i = 0; i < 1025; i++)
{
printf("a");
}
while(1)
;
return 0;
}
编译执行如下图:
看又死循环在那了吧。。。
程序正常退出
行缓存正常退出
要注意哦,这里指的是“正常退出”,非正常退出不会刷新缓存区哦。
下面咱们来写一个验证程序:
#include <stdio.h>
int main(int argc, const char *argv[])
{
printf("a");
return 0;
}
好简单啊,按程序执行顺序来看,printf 将 a 写入了标准输出的缓存区,但是没有写满,不会输出;然后程序往下执行,遇到return0;程序正常退出,此时终端输出了 a;
行缓存非正常退出
那我们修改一下,让它非正常退出,猜一猜 a 还会不会被输出?
#include <stdio.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{
//这个pid的这一行会输出,因为遇到了'\n';
printf("pid = %d\n", getpid());
printf("a");
while(1)
;
return 0;
}
看着眼熟吧,这个程序编译运行后是退出不了的,会一直死循环在while处。然后我们用 kill -9 来杀死这个进程,看看它丫是不是会输出 a !!!
编译运行如下图:
然后我们用 kill -9 2773 这个命令来让程序非正常退出,看看 a 会不会输出;
我们看到了,进程被杀死了,但是a 最后还是没有输出,所以非正常的退出,缓存区不会刷新;
全缓存正常退出
我们再来写一个全缓存的验证程序:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define N 32
int main(int argc, const char *argv[])
{
FILE *fp_r = NULL, *fp_w = NULL;
if(argc < 3)
{
printf("usage: ./a.out [file source] [file destination]");
exit(1);
}
if( (fp_r = fopen(argv[1], "r")) == NULL)
{
perror("fopen error r");
exit(1);
}
if( (fp_w = fopen(argv[2], "a")) == NULL)
{
perror("fopen error w");
exit(1);
}
while(fgets(buf, N, fp_r) != NULL)
{
fputs(buf, fp_w);
}
return 0;
}
编译执行后,查看文件中是否有数据写入:
这个程序是正常退出,所以数据会写入到目标文件中去。
全缓存非正常退出
在来看一个非正常退出的例子:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define N 32
int main(int argc, const char *argv[])
{
FILE *fp_r = NULL, *fp_w = NULL;
if(argc < 3)
{
printf("usage: ./a.out [file source] [file destination]");
exit(1);
}
if( (fp_r = fopen(argv[1], "r")) == NULL)
{
perror("fopen error r");
exit(1);
}
if( (fp_w = fopen(argv[2], "a")) == NULL)
{
perror("fopen error w");
exit(1);
}
while(fgets(buf, N, fp_r) != NULL)
{
fputs(buf, fp_w);
}
while(1)
;
return 0;
}
我们先看看被读取文件的大小:
我们看到被读取的文件大小为563字节,小于4K,全缓存也没有被写满;同时没有强制刷新,程序没有正常退出仍在运行,所以目的文件中不会被写入数据,而是会在while处死循环。好我们来验证一下:
接下来我们看看file_destination文件中是否有数据被写入???
最后,我们在用kill -9 3533 杀死这个进程,看看非正常退出时,全缓存会不会写入目标文件???
果不其然,没有写入到文件中。所以非正常退出,不会刷新缓存区。
fflush强制刷新缓存区
下面咱们来验证一下:
刷新行缓存:
#include <stdio.h>
int main(int argc, const char *argv[])
{
printf("hello world");
fflush(stdout);
while(1)
;
return 0;
}
程序编译运行如下图:
基于我们之前的程序经验,如果没有fflush(stdout)的话,终端将不会打印出 hello world 语句,所以fflush刷新了行缓存。然后while仍在处于死循环;
刷新全缓存:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define N 32
int main(int argc, const char *argv[])
{
FILE *fp = NULL;
char buf[N];
if( (fp = fopen("test", "a+")) == NULL)
{
perror("fopen error");
exit(1);
}
while(fgets(buf, N, stdin) != NULL)
{
if(strncmp(buf, "quit", 4) == 0)
{
exit(1);
}
else if(strncmp(buf, "fflush", 6) == 0)
{
fflush(fp);
bzero(buf, N);
}
fputs(buf, fp);
}
return 0;
}
编译运行如下图:
如图所示,test 文件中并没有数据被写进去。全缓存刷新条件:1.被写满;2.正常退出;3.fflush。显然这3个条件都不符合,所以此时hello world这几个字符仍然在全缓存中。
根据代码所示,如果输入fflush的话,将会刷新全缓存并将缓存中的hello world写入到fp所指的test文件中去。并将fflush在缓存中清除。下面咱们来验证一下:
行缓存特有的刷新条件’\n’
#include <stdio.h>
int main(int argc, const char *argv[])
{
printf("hello world\n");
while(1)
;
return 0;
}
编译运行如下图:
看,虽然while在死循环,但是行缓存遇见了’\n’,直接输出了。对吧。。