This article starts with Golang’s file server, then explores what the sendfile system call is, and finally summarizes the usage scenarios for zero-copy.
Build a file server
How to build a zero-copy file server in Golang, here is the complete code.
Well, yes. Two lines of code to implement a file server.
How is the FileServer Handler implemented?
For the Handler that handles file requests, the implementation will be very simple according to our idea: determine the file type: if the request is for a directory, return the directory list; if the request is for a file; then copy the file data to the client via io.Copy
.
But the real implementation is slightly more complicated than I thought.
By tracing the code, a simple flowchart was drawn as follows.
In the last step tcp Write, that is, write the data to the tcp stream. serveFile uses io.CopyN(w, sendContent, sendSize)
When the code is seen here, I feel very satisfied. Because the implementation does not seem to be too different from what we thought.
Next, take a look at the io.CopyN
method.
Let’s look at the familiar io.Copy
method.
Read from src, write to dst. all together the way we want. No problem.
Wait, I think I’ve seen the ReaderFrom interface somewhere?
The tcp connection implements the ReadFrom interface. What exactly does this implementation do?
If the linux binary is compiled, the system call slice is called in the slice method.
If sendfile is called, the internal implementation calls the system call method Sendfile.
What’s so special about sendfile that read/Write isn’t good?
The answer is found in man.
sendfile is used to copy data between two file descriptors. Since the data operation is done in the kernel state, it avoids copying data between the kernel buffer and the user buffer, so it is called the zero-copy technique. It is much more efficient than the read/write method, which requires a buffered copy.
How it works
Cautions
sendfile must be a file descriptor that supports the mmap function; the target fd must be a socket.