Unix five IO models
- What are the IO models for Unix? What are the differences?
- What are the characteristics of various IO models?
- What’s the difference between blocking and non-blocking?
- What’s the difference between synchronous and asynchronous?
- Why is epoll more efficient?
IO model
Unix provides five IO models, namely blocking IO, non-blocking IO, I/O multiplexing, signal-driven IO, and asynchronous IO.
An IO operation usually consists of two phases:
- Wait for data to be ready;
- Copying data from the kernel to the process
For an input operation on a socket, the first step usually involves waiting for data to arrive from the network. When the waiting group arrives, it is copied to a buffer in the kernel. The second step is to copy the data from the kernel buffer to the application process buffer.
The actual application can be divided into five I/O models, which are blocking or non-blocking when the system call completes the above two steps, and the operating system can deal with the application request synchronously or asynchronously.
* Note: The recvfrom function (which receives data through the Socket) is treated as a system call here
Blocking IO
- Using system calls and blocking until the kernel is ready, and then copying from the kernel buffer to user state, nothing can be done while waiting for the kernel to be ready
- The following function call blocks until the data is ready and copied from the kernel to the user program. This IO model is blocking IO.
Advantages: simple program, process/thread hang during the blocking waiting data, basically does not occupy CPU resources.
Disadvantages: Each connection needs to be handled by an independent process/thread. When the number of concurrent requests is large, the overhead of memory and thread switching is large for maintaining the program. This model is rarely used in actual production.
Non-blocking IO
- The kernel returns an error code when data is not ready, and instead of sleeping, the calling program constantly polls the kernel to see if the data is ready
- The following function is called if the data is not ready, instead of being blocked like blocking IO, it returns an error code. When the data is ready, the function returns successfully.
- The application calls such a non-blocking descriptor loop as polling.
- Polling for non-blocking IO is cpu-intensive and is usually used on a system dedicated to a particular function. You can use this feature by setting non-blocking for the descriptor properties of the socket
Advantages: No blocking in the kernel waiting data process, each INITIATED I/O request can be returned immediately, no blocking waiting, real-time performance.
Disadvantages: Polling constantly asks the kernel, which takes up a lot of CPU time and is low on system resource utilization, so Web servers generally don’t use this I/O model.
I/O multiplexing
- Similar to non-blocking, except that polling is not performed by the user thread, but by the kernel. When the kernel listener listens to the data, it calls the kernel function to copy the data to the user state
- The select system call, which acts as the proxy class, polls all the file descriptors registered with it that require IO. When it has results, it tells the recvFROM function that it wants to fetch the data itself
- IO multiplexing has at least two system calls. If there is only one proxy object, the performance is not as good as the previous IO model, but it is better because it can listen on many sockets at the same time
Mainly select and epoll. A thread can listen on multiple I/O ports and send read/write events to specific threads for processing. The model is as follows:
Advantages: You can wait ready on multiple descriptors at the same time, based on a blocking object, rather than using multiple threads (one thread per file descriptor), which can greatly save system resources.
Disadvantages: When the number of connections is small, the efficiency is lower than the multi-thread + blocking I/O model, and the latency may be larger, because the processing of a single connection requires two system calls, which will increase the elapsed time.
Signal Driven I/O (SIGIO)
Signal-driven I/O: First we allow the Socket to do signal-driven I/O, install a signal handler, and the process continues to run without blocking.
- Using signals, the kernel notifies when data is ready
- First turn on the signal-driven IO socket and use the SIGAction system call to install the signal handler. The kernel returns directly without blocking the user mode
- When the data is ready, the kernel sends a SIGIO signal, which starts IO operations
Advantages: Threads are not blocked while waiting for data, which improves resource utilization.
Disadvantages: Signal I/O may fail to be notified due to signal queue overflow during a large number of I/O operations.
Signal-driven I/O, though, is useful for handling UDP sockets, where such signal-driven notifications mean arrival of a datagram, or return of an asynchronous error.
For TCP, however, signal-driven I/O is almost useless because there are so many conditions that lead to such notifications, each one of which consumes a lot of resources and loses its advantage over the previous approaches.
Asynchronous I/O
- Asynchronous IO relies on signal handlers for notifications
- The difference between the asynchronous I/O model and the previous one is that both blocking and non-blocking are performed in the data preparation phase. The asynchronous I/O model notifies the completion of the I/O operation rather than the completion of data preparation
- Asynchronous IO is truly non-blocking, with the main process doing its own thing and processing the data through callback functions when the IO operation is complete (data is successfully copied from the kernel cache to the application buffer)
Unlike synchronous I/OS, asynchronous I/OS are not executed sequentially. After the aiO_read system call is made by the user process, the kernel data is returned directly to the user process, whether it is ready or not, and the user process can do something else. Once the socket data is ready, the kernel copies the data directly to the process and sends notifications from the kernel to the process. In both the IO and IO phases, processes are non-blocking. The asynchronous process is shown below:
Advantages: Asynchronous I/O can take full advantage of DMA features, allowing I/O operations to overlap with calculations.
Cons: The operating system needs to do a lot of work to implement true asynchronous I/O. True asynchronous I/O is currently implemented through IOCP under Windows.
Comparison of five IO models
Comparison between blocking and non-blocking: after calling blocking I/O, the process will wait for the corresponding process to complete, while non-blocking I/O will not wait for the corresponding process to complete, and directly return when the kernel is still preparing data. The biggest difference between the two is whether the caller waits until the request is received and the result is returned.
Blocking is when the caller waits and does nothing else; Non-blocking means that the caller does something else first.
Blocking and non-blocking talk about callers who call IO;
The differences between synchronous I/O and asynchronous I/O are as follows: When performing I/O operations on a synchronous I/O, the process is blocked. Blocking I/O, non-blocking I/O, I/O multiplexing, and signal-driven I/OS are all synchronous I/OS. Because of these models, while non-blocking IO does not block when polling is ready, all three models block when reading data. In asynchronous I/O mode, a process sends an I/O request and returns that it is not handling money until the kernel notifies the process that the I/O is complete.
Synchronous and asynchronous talk about the called;
As we can see from the figure above, the further you go, the less congestion, and the theoretically optimal efficiency.
Of these five I/O models, the first four are synchronous I/O because the real I/O operations (RECvFROM) block the process/thread, and only the asynchronous I/O model matches the POSIX-defined asynchronous I/O.
More on IO multiplexing
There are several main functions epoll, select, poll.
- I/O multiplexing must be used when customers are dealing with multiple descriptors (typically interactive inputs and network sockets).
- When a customer is working on multiple sockets simultaneously, which is possible but rare.
- If a TCP server handles both listening and connected sockets, I/O multiplexing is also typically used.
- If a server handles both TCP and UDP, it typically uses I/O multiplexing.
- If a server handles multiple services or protocols, I/O multiplexing is typically used.
select
The file descriptors monitored by the SELECT function are divided into three categories, namely writefds, READFds, and Exceptfds. The select function blocks until a descriptor is ready (data readable, writable, or except), or a timeout (timeout specifies the wait time, or null if returned immediately), and the function returns. When the select function returns, the ready descriptor can be found by iterating through the FDset.
poll
Poll in essence and select no difference, it will be the user of the incoming array copy into the kernel space, then the query each fd corresponding device status, if the equipment is ready the equipment for adding a queue and continue to traverse, if not found ready after traverse all fd equipment, hang up the current process, until the device is ready or timeout, It is woken up to iterate over the FD again. This process went through a lot of unnecessary traversal.
epoll
Epoll supports both horizontal and edge triggers, but edge triggers tell the process only once which FDS have just become ready. Another feature is that epoll uses “event” ready notification, registering a FD with epoll_ctl. Once the FD is ready, the kernel uses a callback-like callback mechanism to activate the FD and epoll_wait is notified.
Reference
- Unix Network Programming Volume 1
- Introduction and comparison of five IO models
- Read the I/O model in high performance network programming
- Select poll and epoll for IO multiplexing