认真理一理这些奇怪缓冲问题

上一篇《不可不知的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),回复:领取学习资料进入技术交流群。网盘资料有如下:

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值