上一篇《不可不知的Linux中三种缓冲模式》中说到了三种缓冲类型,这一篇主要讲与缓冲相关的函数,这些函数可以修改默认的缓冲类型,及在实际中可能遇到的问题。
与缓冲相关的函数
我们知道标准错误永远是无缓冲的。当标准输入输出指向的是交互式设备(如终端)的时候,它们是行缓冲的。若不是则是全缓冲的。
那么这些默认的缓冲类型如何修改?其实可以通过这些函数修改:
#include<stdio.h>
void setbuf(FILE *stream, char *buf);
void setbuffer(FILE *stream, char *buf, size_t size);
void setlinebuf(FILE *stream);
int setvbuf(FILE *stream,char *buf, int mode, size_t size);
参数说明如下:
stream FILE *类型,文件指针
buf 缓冲区指针
mode 缓冲模式,包括_IOFBF(全缓冲),_IOLBF(行缓冲),_IONBF(不带缓冲)
size 缓冲区大小
setbuf()的缓冲区长度必须为BUFSIZ(定义在stdio.h),否则可能会出现缓冲区溢出。setbuffer可以指定缓冲区大小。
void setbuf ( FILE * stream, char * buffer );
//同setbuf,但可指定缓冲区大小
void setbuffer(FILE *stream, char *buf, size_t size);
使用setbuf函数打开或者关闭缓冲,当buf是一个有效缓冲区时,此时缓冲打开,若流指向的是终端设备,则此时该流是行缓冲的,否则该流是全缓冲的;当buf为NULL的时候,表示关闭该缓冲。
将buffer指定为NULL,关闭标准输出缓冲。
#include <stdio.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
setbuf(stdout,NULL);
printf("程序猿编码");
sleep(10);
return 0;
}
编译运行
通过设置stdout(标准输出)的第二个参数为NULL,将其变成了不带缓冲,因此你运行后发现,printf的打印会立即显示在终端。
使用setvbuf可以精确的说明缓冲的类型,这里是使用mode来说明的,mode的值包括以下几个
_IOFBF 全缓冲
_IOLBUF 行缓冲
_IONBUF 无缓冲
如果指定一个不带缓冲的流,则忽略buf和size参数。如果指定缓冲,则buf和size分别指定一个缓冲区域和缓冲区域的长度。若此时buf为NULL,则标准IO库将自动制定一个适合长度的缓冲区。
#include <stdio.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
setvbuf(stdout,NULL,_IONBF,0);
printf("程序猿编码");
sleep(10);
return 0;
}
编译运行
设置为不带缓冲
标准I/O库不进行任何字符缓冲,任何读写都是即时可见的。linux下标准错误输出默认是不缓冲,来看一个例子:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
//setbuf(stdout,NULL);
fputs("hello", stderr);
sleep(2);
fputs("程序猿编码", stderr);
sleep(2);
return 0;
}
编译运行
编译执行上面的程序,结果就比较显而易见了。程序一执行的时候就会输出”hello“,过两秒输出”程序猿编码“,再过两秒程序就结束了。
在C语言中,可以通过setbuf来设定无缓冲模式,只要将第二个参数设置为NULL就可以了;
也可以通过setvbuf来设定无缓冲模式,其中_IONBF表示行缓冲,就是IO not buffer的意思。
printf打印的日志没有输出
不知道你有没有遇到过这样的情况,准备调试某一个bug,发现每次运行到某个地方,打印就结束了,然后就挂了,让你误以为程序执行到打印的地方就结束了。
然而有可能程序执行到后面,只是由于打印是行缓冲的,导致部分打印没有出来,很可能就是你没有加上换行符打印而已。
这时候你可以设置为不带缓冲,或者关键位置fflush,或者打印记得加上换行符。
当然你还可以用GDB《GDB调试入门,看这篇就够了!》
控制文件I/O的内核缓冲
在某些情况下,我们可能需要强制将内核缓冲区内的数据刷新至磁盘,而不必等待内核线程等待特定时间后才写入。此时主要有两种选择
1、使用fsync,fdatasync, sync系统调用中的某一个将内核缓冲区的数据强制写入磁盘
2、以O_SYNC同步方式调用open打开文件,此后每次读写操作都会自动立即写入磁盘
#include <unistd.h>
int fsync(int fd);
int fdatasync(int fd);
void sync(void);
fsync保证同步I/O文件完整性,fdatasync保证同步I/O数据完整性。
两个完整性的区别在于前者会将所有更新的文件元数据写入磁盘,后者不会传递所有经过修改的文件元数据属性(如:时间戳)。
fsync与fdatasync均是刷新指定文件流数据,而sync()函数会更新所有内核缓冲区数据至磁盘,对应shell指令sync。
参考:《Linux/UNIX系统编程手册》
欢迎关注公众号【程序猿编码】,添加本人微信号(17865354792),回复:领取学习资料。进入技术交流群。网盘资料有如下: