Today I was fixing a bug for the logger in a project that I’m currently working on. The bug is that when the process is killed or it crashed, the logger sometimes doesn’t write all the logs into the log file with the
fprintf function call. I knew this is an I/O buffer problem, but I didn’t know too much about how to control it and what are the I/O buffering modes besides flushing the buffer every time. Therefore, I decided to investigate about it.
In C/C++, there are three buffering modes. They are full buffering (
_IOFBF), line buffering (
_IOLBF), and no buffering (
_IONBF). From C++ reference,
Full buffering: On output, data is written once the buffer is full (or flushed). On Input, the buffer is filled when an input operation is requested and the buffer is empty.
Line buffering: On output, data is written when a newline character is inserted into the stream or when the buffer is full (or flushed), whatever happens first. On Input, the buffer is filled up to the next newline character when an input operation is requested and the buffer is empty.
No buffering: No buffer is used. Each I/O operation is written as soon as possible. In this case, the buffer and size parameters are ignored.
stdout is in line buffering mode,
stderr is in no buffering mode, and normal files are in full buffering mode. If you want to force a write, you could of course use the
fflush function to flush the buffer. However, sometimes changing the mode instead would be more handy. For example, for the logger in the project, setting the file buffer to line buffering mode is perfect because logs are written in lines.
To change the buffering mechanism, the
setvbuf function should be used (available in
stdio.h). This function is actually used to assign a buffer to a
FILE object, but in the meanwhile it allows you to specify the mode for the buffer. The function has the following signature
int setvbuf( FILE * stream, char * buffer, int mode, size_t size);
If you want the system to allocate a buffer automatically, set
null. Control the behavior of the buffer by setting
size. The return value indicates whether a buffer is successfully assigned to the
stream. Notice that you should call this function after you open a file and before any I/O is incurred.
In this post, I explained the different buffering modes available to C/C++ stream buffers and how they could be set via the
setvbuf function. I hope now you have a better understanding about these mechanisms and are able to fix buffer related problems in C/C++ programs.