1 Kernel space and user space
To prevent User processes from directly operating the Kernel and ensure Kernel security, the operating system divides the memory (virtual memory) into two parts: kernel-space and user-space. In Linux, the kernel module runs in the kernel space, and the corresponding process is in kernel state. The user program runs in user space and the corresponding process is in user mode.
- The core of the operating system is the kernel program, which is independent of the ordinary application program. It has access to both the protected kernel space and the hardware device, while the ordinary application program does not have such access.
- Kernel space always resides in memory and is reserved for the operating system’s kernel.
- Applications are not allowed to read or write directly from the kernel-space region, nor are they allowed to call functions defined by kernel code directly.
- Each application process has a separate user space. The corresponding process is in user mode. The user mode process cannot access the data in the kernel space, nor can it directly call the kernel function.
Kernel-mode processes can execute arbitrary commands and call all resources of the system, while user-mode processes can only perform simple operations and cannot directly call system resources
How does a user-mode process perform a system call? The answer is: a user-mode process must issue instructions to the kernel via a System Call to do things like Call System resources.
2 Read And Write
IO reads and writes of user programs depend on the underlying IO reads and writes, and basically use the underlying read and write system calls. While the names and forms of the read and write system calls may not be exactly the same on different operating systems, their basic functionality is the same.
The read system call at the operating system level does not read data directly from the physical device into the application’s memory, nor does the write system call write data directly to the physical device. Upper-layer applications involve buffers whether they call read or write from the operating system. Specifically, the upper-layer application copies data from the kernel buffer to the application’s process buffer through the operating system’s read system call and from the application’s process buffer to the operating system’s kernel buffer through the operating system’s write system call.
The IO operations of the application are not actually reads and writes at the physical device level, but rather copies of the cache. Neither read nor write system calls are responsible for exchanging data between kernel buffers and physical devices such as disks, network cards, and so on. This low-level read-write swap operation is performed by the operating system Kernel.
Therefore, in the application program, whether the IO operation of socket or file IO operation, all belong to the development of the upper application, they are in the Input and Output dimensions of the execution process is similar, are in the kernel buffer and process buffer between the data exchange.
Kernel buffer and process buffer
The purpose of buffers is to reduce frequent physical exchanges with devices.
There is a very big gap between the external physical devices of the computer and memory and CPU, and the direct read and write of the external devices involves the interruption of the operating system. When a system interruption occurs, you need to save the previous process data and status. After the interruption, you need to restore the previous process data and status. In order to reduce the time loss and performance loss caused by the frequent interruption of the underlying system, the kernel buffer appeared.
The operating system monitors the kernel buffer and interrupts I/O devices when the buffer reaches a certain amount. In this way, I/O operations on physical devices are performed to improve system performance.
When upper-layer applications use read system calls, they simply copy data from the kernel buffer to the application buffer (the process buffer); When upper-layer applications use write system calls, they simply copy data from the application buffer to the kernel buffer.
On Linux, the operating system kernel has only one kernel buffer. Each user program (process) has its own separate buffer, called the user buffer or process buffer. In most cases, the IO reads and writes of Linux user programs do not perform actual IO operations, but directly exchange data between the user buffer and the kernel buffer.
3 I/O Invocation process
The read call copies data from the kernel buffer to the application’s user buffer, and the write call copies data from the application’s user buffer to the kernel buffer:
Take the read system call as an example to complete the two stages of the input process:
- The application waits for the data to be ready.
- Copy data from the kernel buffer to the user buffer.
If a socket is read, the process of the preceding two phases is as follows:
- The application waits for the data to reach the nic over the network, and when the waiting packet arrives, the data is copied by the operating system into the kernel buffer. This work is done automatically by the operating system without the user’s awareness.
- The kernel copies data from the kernel buffer to the application’s user buffer.
If a socket request and response (including read and write) is exchanged between the Java client and server, the complete process is as follows:
- Client sends requests: Java client programs copy data to the kernel buffer through write system calls, and Linux sends the request data from the kernel buffer through the client machine’s network card. On the server side, the request data is read from the receiving network card into the kernel buffer of the server machine.
- Server fetch request: The Java server program reads data from the Linux kernel buffer through the read system call and feeds it into the Java process buffer.
- Server-side business processing: The Java server performs the business processing of the client’s request in its own user space.
- Server returns data: After the Java server finishes processing, the constructed response data is written from the user buffer to the kernel buffer, using the write system call, and the operating system takes care of sending the kernel buffer data out.
- Send to client: The server Linux system writes the data in the kernel buffer to the network adapter. The network adapter sends the data to the target client through the underlying communication protocol.
4 Four main IO models
4.1 Blocking I/O and non-blocking I/O
- Blocking IO means that the kernel needs to complete the IO operation before returning to user space to execute the operation instructions of the user program.
- Non-blocking IO (Non – Blocking IO, NIO) refers to the user space program does not need to wait for the kernel IO operation is completely finished, you can immediately return to the user space to perform subsequent instruction, which launched the IO request of the user process (or thread) in a non-blocking condition, at the same time, the kernel will immediately returned to the user an IO status value.
Blocking is when the user process (or thread) waits and cannot do anything else; Non-blocking means that the user process (or thread) gets the state value returned by the kernel and returns to its own space to do other things.
4.2 Synchronous and Asynchronous
- Synchronous I/O means that the user space (process or thread) initiates THE I/O request actively, and the system kernel is the passive receiver.
- Asynchronous IO, on the other hand, the system kernel is the active initiator of IO requests and the user space is the passive receiver.
4.3 Four main IO models
Synchronous asynchronous. Together, there are four IO models:
-
Blocking IO
Blocking IO refers to a user space (or thread) that initiates a Blocking IO operation and waits for the kernel I/O operation to complete before returning to user space. During an I/O operation, the user process (or thread) that initiates an I/O request is blocked.
-
Synchronize non-blocking IO
Synchronous non-blocking I/O refers to an I/O operation initiated by a user process and immediately returned to user space without waiting for the kernel I/O operation to complete. During an I/O operation, the user process (or thread) that initiates an I/O request is in a non-blocking state.
Tips: Synchronous non-blocking IO can also be called NIO, but it is not NIO in Java programming. NIO (New IO) library components in Java programming belong not to the NIO model of the base IO model, but to the IO multiplexing model.
-
IO multiplexing (asynchronous blocking IO)
To improve performance, the operating system introduced a new system call specifically for querying the ready state of IO file descriptors (including socket connections). On Linux systems, the new system call is the SELECT /epoll system call. Through the system call, a user process (or thread) can monitor multiple file descriptors, once a descriptor ready (usually the kernel buffer to read/write), the kernel file descriptor ready state will be returned to the user process (or thread), user space can be according to the file descriptor ready state for the corresponding I/o system calls. IO Multiplexing belongs to a classical Reactor pattern implementation, sometimes called asynchronous blocking IO. Selector in Java belongs to this model.
-
Asynchronous I/o
Asynchronous IO (AIO) refers to user-space threads becoming passive receivers and kernel space becoming active callers. In the asynchronous I/O model, when the user thread receives the notification, the data has been read by the kernel and placed in the user buffer, and the kernel notifies the user thread to use it directly after the I/O is completed. Asynchronous IO is similar to the typical callback pattern in Java. The user process (or thread) registers various IO event callback functions with the kernel space, and the kernel calls them actively.