10 minutes to read, Java NIO understand the underlying principles of the 10 minutes, the underlying principles of the Java NIO

This article is reproduced from: 10 minutes to understand the underlying principles of the Java NIO

1.1. The Java  IO read and write principle

Socket read or write either a file in Java application development or level of the underlying linux system development, are all input processing input and output of output, referred to as IO read and write. And in principle the processing flow is the same. The difference is that different parameters.

IO user program to read and write, and will be used basically read & write two system calls. May be different operating systems, the name is not exactly the same, but the functionality is the same.

A first emphasize the basics: read system call, not the data from the physical device, read data directly into memory. write system call, the data is not directly written to the physical device.

read system call, it is to copy the data from the kernel buffer to the process buffer; and write system call, the process is to copy the data from the buffer to the kernel buffer. The two system calls, are not responsible for exchanging data between the kernel buffer and disk. Write the underlying exchange, it is done by the operating system kernel kernel.

1.1.1 kernel buffer and buffer process

The purpose of the buffer is to reduce the frequency of system IO calls. As we all know, the system calls the process data and status information needs to be saved before, and after the call back information needed to restore the previous, in order to reduce this loss of time, loss of performance of the system call, so there is a buffer.

With the buffer, the operating system read function to copy the data from the kernel buffer to the process buffer, write the data replication process from the buffer to the kernel buffer. While waiting for the buffer reaches a certain number, then call the IO enhance performance. As for when to read and store the decision by the kernel, the user does not need to be concerned about.

In linux system, the kernel also has a buffer called the kernel buffer. Each process has its own independent buffer, called the process buffer.

Therefore, the user program IO read and write procedures, in most cases, there is no actual IO operation, but in the process of reading and writing its own buffer.

1.1.2. Java IO write processes underlying

IO user program to read and write, and basically use the system call read & write, read to copy the data from the kernel buffer to the process buffer, write the data copied from the kernel buffer to buffer the process, they are not equivalent to the data in exchange between the kernel buffer and disk.

Here Insert Picture Description

First look at the typical process of a typical Java server-side processing network requests:

(1) client requests

Linux through the network card, the customer data read request off, the read data to the kernel buffer.

(2) a data acquisition request

Server reads data from the buffer to the Java process kernel buffer.

(1) business process server

Java server in your own user space, handling client requests.

(2) the data returned from the server

Java server has built a good response, the write buffer system from the user buffer.

(3) to the client

Linux kernel via the network I / O, the kernel data buffer, write the NIC, by the underlying communication protocol, sends the data to the target client.

1.2. The  four main IO model

Server-side programming often need to construct a high-performance IO model, there are four common IO model:

(1) synchronous blocking IO (Blocking IO)

First of all, explain where the blocking and non-blocking:

Blocking IO, referring to the need to complete a thorough Core IO operation, before returning to user space, perform user operation. Blocking refers to the execution status of the user space program, space program user needs to wait for IO operations fully completed. The traditional IO models are synchronous blocking IO. In java, created by default socket are blocked.

Second, explain the synchronous and asynchronous:

Synchronous IO, is a call initiated by way of user space and kernel space. IO synchronization refers to the user space threads are party to initiate IO requests, the kernel space is a passive recipient. Asynchronous IO is, in turn, means the kernel is the kernel initiates party IO request, the user thread is a passive recipient.

(4) non-blocking synchronization IO (Non-blocking IO)

Nonblocking IO, refers to the user does not need to wait for the IO core operation is complete, the kernel immediately returns a status value to the user, the user need not wait for the kernel space IO operation fully completed, the user can immediately return space, the user performs an operation, in non-blocking state.

Simply put: blocking refers to the user space (the calling thread) have been waiting for, and nothing else to do; non-blocking refers to the user space (the calling thread) to get the state to return, IO operation can do your job, do not you can do, shop and go thing.

Requirements socket non-blocking IO is set to NONBLOCK.

We emphasize mentioned here NIO (synchronous non-blocking IO) model, not the Java NIO (New IO) library.

(3) IO multiplexing (IO Multiplexing)

That is a classic Reactor design patterns, sometimes called asynchronous blocking IO, Java in the Selector and Linux epoll is this model.

(5) asynchronous IO (Asynchronous IO)

Asynchronous IO, refer to the invocation user space and kernel space in turn. User space threads are turned into passive acceptance, kernel space is active caller.

This is somewhat similar to Java in the typical pattern is a callback mode, the callback function registered user space threads various IO events to kernel space from the kernel to take the initiative to call.

1.3  synchronous blocking IO (Blocking IO)

In linux Java process, all of the socket by default is blocking IO. In blocking I / O model, the application at the beginning of the IO system calls, until the system call returns, this time it is blocked. After returning successful, the application process begins processing user data cache space.

Here Insert Picture Description

For chestnuts, read initiate a read operation blocking socket system call, the process something like this:

(1) When a user thread calls the read system call, the kernel (kernel) began the first phase of the IO: Prepare data. In many cases, the data has not yet arrived at the beginning (for example, has not yet received a complete packet of Socket), this time the kernel will wait for enough data to come.

(2) When the kernel wait until the data is ready, it will be the data from the kernel kernel buffer, the buffer copied to the user (user memory), then the kernel returns the result.

(3) read from the beginning IO read system call began, the user thread enters the blocked state. It was not until kernel returns the result, user threads before lifting the state of the block, up and running again.

所以,blocking IO的特点就是在内核进行IO执行的两个阶段,用户线程都被block了。

BIO的优点:

程序简单,在阻塞等待数据期间,用户线程挂起。用户线程基本不会占用 CPU 资源。

BIO的缺点:

一般情况下,会为每个连接配套一条独立的线程,或者说一条线程维护一个连接成功的IO流的读写。在并发量小的情况下,这个没有什么问题。但是,当在高并发的场景下,需要大量的线程来维护大量的网络连接,内存、线程切换开销会非常巨大。因此,基本上,BIO模型在高并发场景下是不可用的。

1.4. 同步非阻塞NIO(None Blocking IO)

在linux系统下,可以通过设置socket使其变为non-blocking。NIO 模型中应用程序在一旦开始IO系统调用,会出现以下两种情况:

(1)在内核缓冲区没有数据的情况下,系统调用会立即返回,返回一个调用失败的信息。

(2)在内核缓冲区有数据的情况下,是阻塞的,直到数据从内核缓冲复制到用户进程缓冲。复制完成后,系统调用返回成功,应用进程开始处理用户空间的缓存数据。
Here Insert Picture Description

举个栗子。发起一个non-blocking socket的read读操作系统调用,流程是这个样子:

(1)在内核数据没有准备好的阶段,用户线程发起IO请求时,立即返回。用户线程需要不断地发起IO系统调用。

(2)内核数据到达后,用户线程发起系统调用,用户线程阻塞。内核开始复制数据。它就会将数据从kernel内核缓冲区,拷贝到用户缓冲区(用户内存),然后kernel返回结果。

(3)用户线程才解除block的状态,重新运行起来。经过多次的尝试,用户线程终于真正读取到数据,继续执行。

NIO的特点:

应用程序的线程需要不断的进行 I/O 系统调用,轮询数据是否已经准备好,如果没有准备好,继续轮询,直到完成系统调用为止。

NIO的优点:每次发起的 IO 系统调用,在内核的等待数据过程中可以立即返回。用户线程不会阻塞,实时性较好。

NIO的缺点:需要不断的重复发起IO系统调用,这种不断的轮询,将会不断地询问内核,这将占用大量的 CPU 时间,系统资源利用率较低。

总之,NIO模型在高并发场景下,也是不可用的。一般 Web 服务器不使用这种 IO 模型。一般很少直接使用这种模型,而是在其他IO模型中使用非阻塞IO这一特性。java的实际开发中,也不会涉及这种IO模型。

再次说明,Java NIO(New IO) 不是IO模型中的NIO模型,而是另外的一种模型,叫做IO多路复用模型( IO multiplexing )。

1.5. IO多路复用模型(I/O multiplexing)

如何避免同步非阻塞NIO模型中轮询等待的问题呢?这就是IO多路复用模型。

IO多路复用模型,就是通过一种新的系统调用,一个进程可以监视多个文件描述符,一旦某个描述符就绪(一般是内核缓冲区可读/可写),内核kernel能够通知程序进行相应的IO系统调用。

目前支持IO多路复用的系统调用,有 select,epoll等等。select系统调用,是目前几乎在所有的操作系统上都有支持,具有良好跨平台特性。epoll是在linux 2.6内核中提出的,是select系统调用的linux增强版本。

IO多路复用模型的基本原理就是select/epoll系统调用,单个线程不断的轮询select/epoll系统调用所负责的成百上千的socket连接,当某个或者某些socket网络连接有数据到达了,就返回这些可以读写的连接。因此,好处也就显而易见了——通过一次select/epoll系统调用,就查询到到可以读写的一个甚至是成百上千的网络连接。

举个栗子。发起一个多路复用IO的的read读操作系统调用,流程是这个样子:

Here Insert Picture Description

在这种模式中,首先不是进行read系统调动,而是进行select/epoll系统调用。当然,这里有一个前提,需要将目标网络连接,提前注册到select/epoll的可查询socket列表中。然后,才可以开启整个的IO多路复用模型的读流程。

(1)进行select/epoll系统调用,查询可以读的连接。kernel会查询所有select的可查询socket列表,当任何一个socket中的数据准备好了,select就会返回。

当用户进程调用了select,那么整个线程会被block(阻塞掉)。

(2)用户线程获得了目标连接后,发起read系统调用,用户线程阻塞。内核开始复制数据。它就会将数据从kernel内核缓冲区,拷贝到用户缓冲区(用户内存),然后kernel返回结果。

(3)用户线程才解除block的状态,用户线程终于真正读取到数据,继续执行。

多路复用IO的特点:

IO多路复用模型,建立在操作系统kernel内核能够提供的多路分离系统调用select/epoll基础之上的。多路复用IO需要用到两个系统调用(system call), 一个select/epoll查询调用,一个是IO的读取调用。

和NIO模型相似,多路复用IO需要轮询。负责select/epoll查询调用的线程,需要不断的进行select/epoll轮询,查找出可以进行IO操作的连接。

另外,多路复用IO模型与前面的NIO模型,是有关系的。对于每一个可以查询的socket,一般都设置成为non-blocking模型。只是这一点,对于用户程序是透明的(不感知)。

多路复用IO的优点:

用select/epoll的优势在于,它可以同时处理成千上万个连接(connection)。与一条线程维护一个连接相比,I/O多路复用技术的最大优势是:系统不必创建线程,也不必维护这些线程,从而大大减小了系统的开销。

Java的NIO(new IO)技术,使用的就是IO多路复用模型。在linux系统上,使用的是epoll系统调用。

多路复用IO的缺点:

本质上,select/epoll系统调用,属于同步IO,也是阻塞IO。都需要在读写事件就绪后,自己负责进行读写,也就是说这个读写过程是阻塞的。

如何充分的解除线程的阻塞呢?那就是异步IO模型。

1.6. 异步IO模型(asynchronous IO)

如何进一步提升效率,解除最后一点阻塞呢?这就是异步IO模型,全称asynchronous I/O,简称为AIO。

AIO的基本流程是:用户线程通过系统调用,告知kernel内核启动某个IO操作,用户线程返回。kernel内核在整个IO操作(包括数据准备、数据复制)完成后,通知用户程序,用户执行后续的业务操作。

kernel的数据准备是将数据从网络物理设备(网卡)读取到内核缓冲区;kernel的数据复制是将数据从内核缓冲区拷贝到用户程序空间的缓冲区。

Here Insert Picture Description

(1)当用户线程调用了read系统调用,立刻就可以开始去做其它的事,用户线程不阻塞。

(2)内核(kernel)就开始了IO的第一个阶段:准备数据。当kernel一直等到数据准备好了,它就会将数据从kernel内核缓冲区,拷贝到用户缓冲区(用户内存)。

(3)kernel会给用户线程发送一个信号(signal),或者回调用户线程注册的回调接口,告诉用户线程read操作完成了。

(4)用户线程读取用户缓冲区的数据,完成后续的业务操作。

异步IO模型的特点:

在内核kernel的等待数据和复制数据的两个阶段,用户线程都不是block(阻塞)的。用户线程需要接受kernel的IO操作完成的事件,或者说注册IO操作完成的回调函数,到操作系统的内核。所以说,异步IO有的时候,也叫做信号驱动 IO 。

异步IO模型缺点:

需要完成事件的注册与传递,这里边需要底层操作系统提供大量的支持,去做大量的工作。

目前来说, Windows 系统下通过 IOCP 实现了真正的异步 I/O。但是,就目前的业界形式来说,Windows 系统,很少作为百万级以上或者说高并发应用的服务器操作系统来使用。

In the Linux system, asynchronous IO model was introduced in version 2.6, it is currently not perfect. So, this is under Linux, implementations are multiplexed IO model mode dominated by high concurrent network programming.

Summary about:

Four kinds of IO model, theoretically the more backward, the less obstruction, efficiency is optimal. Of the four I / O model, the first three belong to the synchronous I / O, because the real I / O operation will block the thread. Only the last is the real asynchronous I / O model, but at present the Linux operating system outstanding sound.

Guess you like

Origin www.cnblogs.com/alimayun/p/12057108.html