Why do unbuffered read()/write() operations use buffer cache? - c

Basically on UNIX, read() and write() functions are unbuffered I/O,
and there are Standard I/O, which is buffered I/O.
But, read() and write() functions use buffer cache which is in kernel before doing real I/O(I/O to real device), and real I/O happens using buffer cache. It's using buffer.
I heard unbuffered I/O means I/O happens on char-by-char to real device.
Then, why read() and write() functions are unbuffered I/O, even though it is using buffer cache?

Basically the term "buffering" here means "a place where data is stored when going to/from the kernel", i.e. to avoid doing one system call for each I/O call, the buffered functions use a buffer between.
What the kernel does with the data is not something the standard library can do much about.
It would be possible to do a 1:1 mapping of read/write calls at the standard library's level (i.e. fread() and friends) to read()/write() calls on the underlying file descriptor; the term buffering is telling you that is not what you can expect.

"(Un)buffered" in the manual refers to user-space buffering. Kernel space buffering depends on implementation, usually most devices are buffered (disk, sockets, USB etc.) except hardware ports (GPIO).

Related

what is practical differences between flush, write() and fflush()?

In this post, the answer said
Flushing: To sync the temporary state of your application data with the permanent state of the data (in a database, or on disk).
I think that the flush is executed when some buffer is written to an i/o device (like disk) by the write() system call.
So it seems that a data writing to a device with write() and the data flushing to the device are to do the same things.
If so, can I say that the flushing a data with fflush() and the writing the data with write() are completely same?
First, let's do the obvious thing:
fflush
For output streams (and for update streams on which the last operation was output), writes any unwritten data from the stream's buffer to the associated output device.
The C Standard doesn't state how the data is written to the output device. On Posix systems, most likely via write, other systems might have different (similar) interfaces.
Conceptually speaking, a flush will use the underlying write primitive to transmit the data from the buffer to the output device.
In short:
fflush() the same as write() -> No.
fflush() uses write() -> Yes, most likely.
fflush() and write() ensures the data to be written to the output device -> Yes.

Are fscanf and fprintf buffered in C?

I wish to write an efficient C program, that makes a copy of a file. There doesn't seem to be a function (such as rename) that performs this. I plan to use fscanf an fprintf in stdio.h, but their descriptions do not say how or if they are buffered. Are they buffered between different cache levels? (I assume disk to memory buffer is handled by the OS)
When you open a file with fopen, it will be fully buffered.
You can change the buffering before doing any other operations on the file, using setvbuf (reference).
Using any normal I/O functions on a FILE object will take advantage of the buffering.
If you are just copying the data, you will be doing sequential reads and writes, and will not necessarily need buffering. But doing that efficiently does require choosing an appropriate block size for I/O operations. Traditionally, this is related to the size of a disk sector (4096 bytes) but that value isn't future-proof. The default used by fopen is BUFSIZ.
As with any optimisation, construct actual tests to verify your performance gains (or losses).
In the end, for the fastest I/O you might have to use OS-specific APIs. The C I/O functions just map to the general case of those APIs, but there may be special performance settings for an OS that you cannot control through the C library. I certainly ran into this when writing a fast AVI writer for Windows. Using platform-specific I/O I was able to achieve the maximum read/write speed of the disk: twice the speed of buffered I/O (<stdio.h>) or the native AVI API, and about 20% faster than traditional C unbuffered I/O.
The printf and scanf family of functions are all part of the same buffered "interface". man 3 stdio:
The standard I/O library provides a simple and efficient buffered
stream I/O interface. Input and output is mapped into logical data
streams and the physical I/O characteristics are concealed. The
functions and macros are listed below; more information is
available from the individual man pages.
If you want to avoid buffering, you will have to use a different C library.

what is the point of using the setvbuf() function in c?

Why would you want to set aside a block of memory in setvbuf()?
I have no clue why you would want to send your read/write stream to a buffer.
setvbuf is not intended to redirect the output to a buffer (if you want to perform IO on a buffer you use sprintf & co.), but to tightly control the buffering behavior of the given stream.
In facts, C IO functions don't immediately pass the data to be written to the operating system, but keep an intermediate buffer to avoid continuously performing (potentially expensive) system calls, waiting for the buffer to fill before actually performing the write.
The most basic case is to disable buffering altogether (useful e.g. if writing to a log file, where you want the data to go to disk immediately after each output operation) or, on the other hand, to enable block buffering on streams where it is disabled by default (or is set to line-buffering). This may be useful to enhance output performance.
Setting a specific buffer for output can be useful if you are working with a device that is known to work well with a specific buffer size; on the other side, you may want to have a small buffer to cut down on memory usage in memory-constrained environments, or to avoid losing much data in case of power loss without disabling buffering completely.
In C files opened with e.g. fopen are by default buffered. You can use setvbuf to supply your own buffer, or make the file operations completely unbuffered (like to stderr is).
It can be used to create fmemopen functionality on systems that doesn't have that function.
The size of a files buffer can affect Standard library call I/O rates. There is a table in Chap 5 of Steven's 'Advanced Programming in the UNIX Environment' that shows I/O throughput increasing dramatically with I/O buffer size, up to ~16K then leveling off. A lot of other factor can influenc overall I/O throughtput, so this one "tuning" affect may or may not be a cureall. This is the main reason for "why" other than turning off/on buffering.
Each FILE structure has a buffer associated with it internally. The reason behind this is to reduce I/O, and real I/O operations are time costly.
All your read/write will be buffered until the buffer is full. All the data buffered will be output/input in one real I/O operation.
Why would you want to set aside a block of memory in setvbuf()?
For buffering.
I have no clue why you would want to send your read/write stream to a buffer.
Neither do I, but as that's not what it does the point is moot.
"The setvbuf() function may be used on any open stream to change its buffer" [my emphasis]. In other words it alread has a buffer, and all the function does is change that. It doesn't say anything about 'sending your read/write streams to a buffer". I suggest you read the man page to see what it actually says. Especially this part:
When an output stream is unbuffered, information appears on the destination file or terminal as soon as written; when it is block buffered many characters are saved up and written as a block; when it is line buffered characters are saved up until a newline is output or input is read from any stream attached to a terminal device (typically stdin).

Write system call writes data to disk directly?

I've read couple of questions(here) related to this but I still have some confusion.
My understanding is that write system call puts the data into Buffered Cache(OS caches as referred in that question). When the Buffered Cache gets full it is written to the disk.
Buffered IO is further optimization on top of this. It caches in the C RTL buffers and when they get full a write system call issued to move the contents to Buffered Cache. If I use fflush then data related to this particular file that is present in the C RTL buffers as well as Buffered Cache is sent to the disk.
Is my understanding correct?
How the stdio buffers are flushed is depending on the standard C library you use. To quote from the Linux manual page:
Note that fflush() only flushes the user space buffers provided by the C library.
To ensure that the data is physically stored on disk the kernel buffers must be
flushed too, for example, with sync(2) or fsync(2).
This means that on a Linux system, using fflush or overflowing the buffer will call the write function. But the operating system may keep internal buffers, and not actually write the data to the device. To make sure the data is truly written to the device, use both fflush and the low-level fsync.
Edit: Answer rephrased.

Why the fwrite libc function is faster than the syscall write function?

After providing the same program which reads a random generated input file and echoes the same string it read to an output. The only difference is that on one side I'm providing the read and write methods from linux syscalls, and on the other side I'm using fread/fwrite.
Timing my application with an input of 10Mb in size and echoing it to /dev/null, and making sure the file is not cached, I've found that libc's fwrite is faster by a LARGE scale when using very small buffers (1 byte in case).
Here is my output from time, using fwrite:
real 0m0.948s
user 0m0.780s
sys 0m0.012s
And using the syscall write:
real 0m8.607s
user 0m0.972s
sys 0m7.624s
The only possibility that I can think of is that internally libc is already buffering my input... Unfortunately I couldn't find that much information around the web, so maybe the gurus here could help me out.
Timing my application with an input of
10Mb in size and echoing it to
/dev/null, and making sure the file in
not cached, I've found that libc's
frwite is faster by a LARGE scale when
using very small buffers (1 byte in
case).
fwrite works on streams, which are buffered. Therefore many small buffers will be faster because it won't run a costly system call until the buffer fills up (or you flush it or close the stream). On the other hand, small buffers being sent to write will run a costly system call for each buffer - that's where you're losing the speed. With a 1024 byte stream buffer, and writing 1 byte buffers, you're looking at 1024 write calls for each kilobyte, rather than 1024 fwrite calls turning into one write - see the difference?
For big buffers the difference will be small, because there will be less buffering, and therefore a more consistent number of system calls between fwrite and write.
In other words, fwrite(3) is just a library routine that collects up output into chunks, and then calls write(2). Now, write(2), is a system call which traps into the kernel. That's where the I/O actually happens. There is some overhead for simply calling into the kernel, and then there is the time it takes to actually write something. If you use large buffers, you will find that write(2) is faster because it eventually has to be called anyway, and if you are writing one or more times per fwrite then the fwrite buffering overhead is just that: more overhead.
If you want to read more about it, you can have a look at this document, which explains standard I/O streams.
write(2) is the fundamental kernel operation.
fwrite(3) is a library function that adds buffering on top of write(2).
For small (e.g., line-at-a-time) byte counts, fwrite(3) is faster, because of the overhead for just doing a kernel call.
For large (block I/O) byte counts, write(2) is faster, because it doesn't bother with buffering and you have to call the kernel in both cases.
If you look at the source to cp(1), you won't see any buffering.
Finally, there is one last consideration: ISO C vs Posix. The buffered library functions like fwrite are specified in ISO C whereas kernel calls like write are Posix. While many systems claim Posix-compatibility, especially when trying to qualify for government contracts, in practice it's specific to Unix-like systems. So, the buffered ops are more portable. As a result, a Linux cp will certainly use write but a C program that has to work cross-platform may have to use fwrite.
You can also disable buffering with setbuf() function. When the buffering is disabled, fwrite() will be as slow as write() if not slower.
More information on this subject can be found there: http://www.gnu.org/s/libc/manual/html_node/Controlling-Buffering.html

Resources