setvbuf缓冲的使用
平时我们在写文件的时候,iofstream也好,fwrite也罢,写文件到磁盘,刷新、落盘,这样就完成了一次磁盘IO交互;
当出现高并发,多个线程都在写磁盘的时候,就可能出现磁盘IO瓶颈,如图,写等待的时间就会很长,这将一定程度阻塞程序的运行或者影响正常存储:
# iostat -x -d /dev/sda -m 1
针对这种问题,解决方法一方面是多加磁盘数量,来均衡单一磁盘的读写压力;另一方面也可以通过IO缓冲机制减少写磁盘的次数;
setvbuf
:
通过设置一个中间缓冲,接收写文件的数据,等到缓冲满了,再刷新到磁盘中;在一定程度上减少fwrite写文件次数;
int setvbuf( FILE *stream, char *buffer, int mode, size_t size); 如果成功,则返回 0
stream
指向 FILE
结构的指针。
buffer
用户分配的缓冲区。
mode
缓冲模式。
size
缓冲区大小(以字节为单位)。 允许的范围:2 <= size
<= INT_MAX (2147483647)。 在内部将为 size
提供的值向下舍入为最接近的 2 的倍数。
setvbuf 函数允许程序控制 stream 的缓冲和缓冲区大小。 stream 必须引用自该文件打开后尚未进行 I/O 操作的打开文件。 将 buffer 所指向的数组用作缓冲区,除非 buffer 是 NULL,在这种情况下,
setvbuf 使用长度为 size/2 * 2 字节的自动分配的缓冲区。 此模式必须是 _IOFBF、_IOLBF 或 _IONBF。 如果 mode 是 _IOFBF 或 _IOLBF,则将 size 用作缓冲区的大小。 如果 mode 是 _IONBF,则该流取消缓冲并同时忽略 size 和 buffer。 mode 的值及其含义是:
setvbuf(fp, filebuf.get(), _IOFBF, BufSize);使用这个缓冲,在fwrite的时候就会先写入缓冲,等到缓冲满再落盘;一定程度上依靠缓冲减少落盘次数;减少磁盘IO;缓解磁盘压力;
这里要注意的是:使用setvbuf作为中间缓冲,如果磁盘满了,fwrite,fflush操作fp向filebuf中写数据是不会报错的,但是IOFBF向硬盘写数据就无法落盘了(IOFBF什么时候落盘代码层并不知道);如果想强行每次写缓冲之后落盘,可以使用fsync,如果此时磁盘满了,fsyn会返回-1;(同步内存中所有已修改的文件数据到储存设备)