preface

Imagine that we are making a chat server, and we perform a read operation on a socket, which returns data written to the socket by the other end. Read is a simple system call to the upper user, but a complex process to the underlying operating system. This read system call executes on the underlying operating system, from waiting for data to be ready on the network, to copying ready data from the network card to the kernel recV buffer, and finally to the user process. We can simplify the process of a read system call into two phases:

  1. Wait for data to be ready (Step 1 / Phase 1)

Imagine calling the System.in terminal in your code and waiting for user input. In the network world, you wait for the client to write data into the socket

  1. Copy ready data from kernel space to user process space (Step 2 / Phase 2)

When the client writes data, the server’s network adapter receives the data first and calls an interrupt to notify the operating system to pick it up. The operating system then copies the data to the server’s process

The following five IO models are available for blocking, non-blocking, and callbacks of the above two steps.

When we talk about blocking and non-blocking, we really mean whether we need to go through phase one: waiting for data to be ready

Five IO models

BIO Blocking IO Model Blocking I/O

BIO is the most common IO model, and when we call read, our thread blocks until the data is ready and the kernel copies it to our process.Recvfrom is the underlying system call when we execute read. It is inefficient to block every time read is called. You can! Let’s move on to NIO.

NIO Nonblocking IO Model Nonblocking IO

When NIO is used, the read operation returns immediately.As you can see from the figure above, the first three system calls return immediately an error of EWOULDBLOCK as there is no data ready, and the user thread returns as the data is already ready after the kernel copies the data to the user process. Applications poll to see if the data is ready until it is, which is usually a waste of CPU.

Multiplex IO Multiplexing Model

IO multiplexing is a relatively mainstream high-performance IO network model, such as Netty,Nginx,Redis and other underlying network models are based on IO multiplexing.

As shown in the figure above, we call SELECT (or other poll, epoll) instead of reading directly. This call will also block and will not return until the FD event is triggered in our select register. We then perform IO operations, such as read, based on the trigger event returned. For read operations, the data is already ready, so it won’t block. On the surface, IO multiplexing takes two system calls, but it allows us to listen for multiple FDS in a single thread. Imagine, in the previous BIO, that we, as a server, were processing read and write requests from n client connections. To prevent one client’s processing from blocking the responses of other clients, we needed one client connection per thread. However, with IO multiplexing, we can listen for multiple client connections on a single select, which means we can handle multiple client connections on a single thread. Of course, in the case of a small number of client connections, the performance of BIO is better than that of IO multiplexing, but when the number of connections is up, the number of threads can be very large if bio1:1 is used (one thread for each client connection). Threads are resource-intensive (typically 512K to 4M of memory in Java), too many threads can result in OOM applications, and frequent switching between threads can be a huge burden on the CPU. In fact, previous studies have discussed these BIO problems, and called it the C10K, C100K problem, interested in the search.

Signal-driven I/O Model Indicates the signal-driven I/O Model

In signal-driven IO, we register the callback function with sigAction on the fd we are interested in. The system call will return immediately so that we can continue processing the business logic in the main thread. When the data is ready, the system calls back to the handler we just registered so that we can read the incoming data without blocking.

The advantage of signal-driven IO is that we don’t have to block waiting for data or poll to see if the data is ready, we just register a callback function in advance and go about our business.

Asynchronous IO Model Asynchronous I/O

In asynchronous IO, aio_read takes arguments such as fd,buffer, and handler and returns as soon as it is called. When the data is ready and has been copied from the kernel to the user process (in this case, the buffer argument), our callback function handler is fired.

As you can see, asynchronous IO and signal-driven IO are very similar, but the only important difference is that signal-driven IO is inData ready (Step 1 completed)When this happens, the kernel still needs to copy the data to the user process, while asynchronous IO is inData ready (Step one+ copy data from kernel to user process (Step 2)Is triggered when the read operation is complete.

IO model comparison and summary

It can be seen from the figure above that the first four IO models are mainly different in the first stage, and the first four IO models are the same in the second stage. However, asynchronous IO differs from the previous four IO models in phase one and phase two.

Asynchronous vs Synchronous

In the POSIX standard, synchronization and asynchrony are defined as follows:

  • Synchronizing the I/O operation blocks the requesting process until the I/O operation completes (both phases one and two complete)
  • Asynchronous I/O operations do not block the requester process

Using this definition, the previous four IO models (BIO\NIO\IO Mutiplexing\ signal-driven IO) are all synchronous I/O, and only the Asynchronous IO Model is Asynchronous I/O

Afterword.

This article is just an appetizer for the IO model series, which will be updated later with select\ Poll \epoll and specific BIO, NIO, and IO multiplexing cases in Java. In addition, much of this article comes from the Unix Network Programming 3rd edition 6.2 IO Models section. Before the holiday, I had not been able to understand the relationship between IO multiplexing and various IO models. When I looked for blogs on the Internet, their quality was uneven and they copied each other. Until I saw the book UNP, I suddenly became enlightened. Here and every reader at the same time also say with oneself: have time to read more primary data, learn knowledge from the source, reduce information distortion. Of course, this blog is not water at all

REF

References:

  1. Unix Network Programming 3rd edition 6.2IO Models

The pictures and layout in the article are mostly from this chapter

  1. The Linux Programming Interface 63 Alternative I/O Models