「java中常见io模型」Javaio模型

博主:adminadmin 2022-11-23 09:26:06 42

今天给各位分享java中常见io模型的知识,其中也会对Javaio模型进行解释,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在开始吧!

本文目录一览:

IO模型及select,poll,epoll和kqueue的区别

(一)首先,介绍几种常见的I/O模型及其区别,如下:

blocking I/O

nonblocking I/O

I/O multiplexing (select and poll)

signal driven I/O (SIGIO)

asynchronous I/O (the POSIX aio_functions)—————异步IO模型最大的特点是 完成后发回通知。

阻塞与否,取决于实现IO交换的方式。

异步阻塞是基于select,select函数本身的实现方式是阻塞的,而采用select函数有个好处就是它可以同时监听多个文件句柄.

异步非阻塞直接在完成后通知,用户进程只需要发起一个IO操作然后立即返回,等IO操作真正的完成以后,应用程序会得到IO操作完成的通知,此时用户进程只需要对数据进行处理就好了,不需要进行实际的IO读写操作,因为真正的IO读取或者写入操作已经由内核完成了。

1 blocking I/O

这个不用多解释吧,阻塞套接字。下图是它调用过程的图示:

重点解释下上图,下面例子都会讲到。首先application调用 recvfrom()转入kernel,注意kernel有2个过程,wait for data和copy data from kernel to user。直到最后copy complete后,recvfrom()才返回。此过程一直是阻塞的。

2 nonblocking I/O:

与blocking I/O对立的,非阻塞套接字,调用过程图如下:

可以看见,如果直接操作它,那就是个轮询。。直到内核缓冲区有数据。

3 I/O multiplexing (select and poll)

最常见的I/O复用模型,select。

select先阻塞,有活动套接字才返回。与blocking I/O相比,select会有两次系统调用,但是select能处理多个套接字。

4 signal driven I/O (SIGIO)

只有UNIX系统支持,感兴趣的课查阅相关资料

与I/O multiplexing (select and poll)相比,它的优势是,免去了select的阻塞与轮询,当有活跃套接字时,由注册的handler处理。

5 asynchronous I/O (the POSIX aio_functions)

很少有*nix系统支持,windows的IOCP则是此模型

完全异步的I/O复用机制,因为纵观上面其它四种模型,至少都会在由kernel copy data to appliction时阻塞。而该模型是当copy完成后才通知application,可见是纯异步的。好像只有windows的完成端口是这个模型,效率也很出色。

6 下面是以上五种模型的比较

可以看出,越往后,阻塞越少,理论上效率也是最优。

=====================分割线==================================

5种模型的比较比较清晰了,剩下的就是把select,epoll,iocp,kqueue按号入座那就OK了。

select和iocp分别对应第3种与第5种模型,那么epoll与kqueue呢?其实也于select属于同一种模型,只是更高级一些,可以看作有了第4种模型的某些特性,如callback机制。

为什么epoll,kqueue比select高级?

答案是,他们无轮询。因为他们用callback取代了。想想看,当套接字比较多的时候,每次select()都要通过遍历FD_SETSIZE个Socket来完成调度,不管哪个Socket是活跃的,都遍历一遍。这会浪费很多CPU时间。如果能给套接字注册某个回调函数,当他们活跃时,自动完成相关操作,那就避免了轮询,这正是epoll与kqueue做的。

windows or *nix (IOCP or kqueue/epoll)?

诚然,Windows的IOCP非常出色,目前很少有支持asynchronous I/O的系统,但是由于其系统本身的局限性,大型服务器还是在UNIX下。而且正如上面所述,kqueue/epoll 与 IOCP相比,就是多了一层从内核copy数据到应用层的阻塞,从而不能算作asynchronous I/O类。但是,这层小小的阻塞无足轻重,kqueue与epoll已经做得很优秀了。

提供一致的接口,IO Design Patterns

实际上,不管是哪种模型,都可以抽象一层出来,提供一致的接口,广为人知的有ACE,Libevent(基于reactor模式)这些,他们都是跨平台的,而且他们自动选择最优的I/O复用机制,用户只需调用接口即可。说到这里又得说说2个设计模式,Reactor and Proactor。见:Reactor模式--VS--Proactor模式。Libevent是Reactor模型,ACE提供Proactor模型。实际都是对各种I/O复用机制的封装。

Java nio包是什么I/O机制?

现在可以确定,目前的java本质是select()模型,可以检查/jre/bin/nio.dll得知。至于java服务器为什么效率还不错。。我也不得而知,可能是设计得比较好吧。。-_-。

=====================分割线==================================

总结一些重点:

只有IOCP是asynchronous I/O,其他机制或多或少都会有一点阻塞。

select低效是因为每次它都需要轮询。但低效也是相对的,视情况而定,也可通过良好的设计改善

epoll, kqueue、select是Reacor模式,IOCP是Proactor模式。

java nio包是select模型。。

(二)epoll 与select的区别

1. 使用多进程或者多线程,但是这种方法会造成程序的复杂,而且对与进程与线程的创建维护也需要很多的开销。(Apache服务器是用的子进程的方式,优点可以隔离用户) (同步阻塞IO)

2.一种较好的方式为I/O多路转接(I/O multiplexing)(貌似也翻译多路复用),先构造一张有关描述符的列表(epoll中为队列),然后调用一个函数,直到这些描述符中的一个准备好时才返回,返回时告诉进程哪些I/O就绪。select和epoll这两个机制都是多路I/O机制的解决方案,select为POSIX标准中的,而epoll为Linux所特有的。

区别(epoll相对select优点)主要有三:

1.select的句柄数目受限,在linux/posix_types.h头文件有这样的声明:#define __FD_SETSIZE 1024 表示select最多同时监听1024个fd。而epoll没有,它的限制是最大的打开文件句柄数目。

2.epoll的最大好处是不会随着FD的数目增长而降低效率,在selec中采用轮询处理,其中的数据结构类似一个数组的数据结构,而epoll是维护一个队列,直接看队列是不是空就可以了。epoll只会对"活跃"的socket进行操作---这是因为在内核实现中epoll是根据每个fd上面的callback函数实现的。那么,只有"活跃"的socket才会主动的去调用 callback函数(把这个句柄加入队列),其他idle状态句柄则不会,在这点上,epoll实现了一个"伪"AIO。但是如果绝大部分的I/O都是“活跃的”,每个I/O端口使用率很高的话,epoll效率不一定比select高(可能是要维护队列复杂)。

3.使用mmap加速内核与用户空间的消息传递。无论是select,poll还是epoll都需要内核把FD消息通知给用户空间,如何避免不必要的内存拷贝就很重要,在这点上,epoll是通过内核于用户空间mmap同一块内存实现的。

关于epoll工作模式ET,LT

epoll有两种工作方式

ET:Edge Triggered,边缘触发。仅当状态发生变化时才会通知,epoll_wait返回。换句话,就是对于一个事件,只通知一次。且只支持非阻塞的socket。

LT:Level Triggered,电平触发(默认工作方式)。类似select/poll,只要还有没有处理的事件就会一直通知,以LT方式调用epoll接口的时候,它就相当于一个速度比较快的poll.支持阻塞和不阻塞的socket。

三 Linux并发网络编程模型

1 Apache 模型,简称 PPC ( Process Per Connection ,):为每个连接分配一个进程。主机分配给每个连接的时间和空间上代价较大,并且随着连接的增多,大量进程间切换开销也增长了。很难应对大量的客户并发连接。

2 TPC 模型( Thread Per Connection ):每个连接一个线程。和PCC类似。

3 select 模型:I/O多路复用技术。

.1 每个连接对应一个描述。select模型受限于 FD_SETSIZE即进程最大打开的描述符数linux2.6.35为1024,实际上linux每个进程所能打开描数字的个数仅受限于内存大小,然而在设计select的系统调用时,却是参考FD_SETSIZE的值。可通过重新编译内核更改此值,但不能根治此问题,对于百万级的用户连接请求 即便增加相应 进程数, 仍显得杯水车薪呀。

.2select每次都会扫描一个文件描述符的集合,这个集合的大小是作为select第一个参数传入的值。但是每个进程所能打开文件描述符若是增加了 ,扫描的效率也将减小。

.3内核到用户空间,采用内存复制传递文件描述上发生的信息。

4 poll 模型:I/O多路复用技术。poll模型将不会受限于FD_SETSIZE,因为内核所扫描的文件 描述符集合的大小是由用户指定的,即poll的第二个参数。但仍有扫描效率和内存拷贝问题。

5 pselect模型:I/O多路复用技术。同select。

6 epoll模型:

.1)无文件描述字大小限制仅与内存大小相关

.2)epoll返回时已经明确的知道哪个socket fd发生了什么事件,不用像select那样再一个个比对。

.3)内核到用户空间采用共享内存方式,传递消息。

四 :FAQ

1、单个epoll并不能解决所有问题,特别是你的每个操作都比较费时的时候,因为epoll是串行处理的。 所以你有还是必要建立线程池来发挥更大的效能。

2、如果fd被注册到两个epoll中时,如果有时间发生则两个epoll都会触发事件。

3、如果注册到epoll中的fd被关闭,则其会自动被清除出epoll监听列表。

4、如果多个事件同时触发epoll,则多个事件会被联合在一起返回。

5、epoll_wait会一直监听epollhup事件发生,所以其不需要添加到events中。

6、为了避免大数据量io时,et模式下只处理一个fd,其他fd被饿死的情况发生。linux建议可以在fd联系到的结构中增加ready位,然后epoll_wait触发事件之后仅将其置位为ready模式,然后在下边轮询ready fd列表。

干货 五种IO模型的特点以及比较

为了保证操作系统的安全,将内存划分为内核空间和用户空间。内核空间的进程,可以访问硬件执行IO等操作,用户空间的进程只能通过系统调用来访问IO等系统资源。

对于一次IO访问(以read举例),数据会先被拷贝到操作系统内核的缓冲区中,然后再拷贝到应用程序的地址空间。

所以,当一个read操作发生时,它会经历两个阶段

正是因为这两个阶段,linux系统产生了下面五种网络模型:

在linux下,默认情况下所有的socket都是blocking的,流程如下图所示:

进程调用recvfrom系统调用来读取数据,这是如果还没有到达,进程就进入阻塞状态。等数据到达后完成到内核去的拷贝,再从内核拷贝到用户空间,用户进程才解除阻塞状态。

特点:在IO执行的两个阶段进程都会都阻塞

执行非阻塞io系统调用时,如果内核中的数据还没有准备好,会直接返回,不会阻塞。通过进程不断查询,直到数据在内核中就绪,便开始拷贝到用户空间。拷贝的过程中,进程还是被阻塞了,所有非阻塞IO也是同步IO。

特点:需要进程不断地主动询问kernel数据是否准备好了

单个进程处理多个网络连接IO,使用select\poll\epoll三种系统调用,不断轮询所有的连接,如果有数据到达内核则通知进程,进行数据拷贝到用户内存。

当调用select时,进程会进入阻塞状态,直到有数据到达。

这个图看起来和阻塞IO区别不大,甚至还多使用了一个系统调用

但它的优势在于可以同时监控多个IO连接。

所以,如果连接数不是很高的话,使用select/epoll的web server不一定比使用multi-threading + blocking IO的web servet性能更好。多路IO复用的优势并不是对于单个连接能处理的更快,而在于能处理更多的连接。

linux下的异步io使用的很少,其流程如下:

发起read操作后进程立马返回,整个Io过程不会产生任何block。kernel会等等数据准备完成,然后将数据拷贝到用户内存。当这一切都完成后,kernel会给用户进程发送一个signal,告诉它read操作完成了。

调用blocking io会一直block进程直到操作完成

no-blocking io在kernel准备数据的阶段是会立刻返回的

只有异步IO是异步IO,

其他3种:阻塞IO、非阻塞IO、多路复用IO都是同步的。

这是因为其他三种IO在执行真实IO操作的过程中都有进程阻塞的阶段,而异步IO在整个过程中进程都没有被阻塞。非阻塞IO在内核数据就绪,拷贝到用户空间的阶段也是阻塞的,因此也是同步IO。

Linux IO模式及select、poll、epoll详解

服务器常见I/O模型

1.同步阻塞IO(Blocking IO)

首先,解释一下这里的阻塞与非阻塞:阻塞IO,指的是需要内核IO操作彻底完成后,才返回到用户空间执行用户的操作。阻塞指的是用户空间程序的执行状态。传统的IO模型都是同步阻塞IO。在Java中,默认创建的socket都是阻塞的。其次,解释一下同步与异步:同步IO,是一种用户空间与内核空间的IO发起方式。同步IO是指用户空间的线程是主动发起IO请求的一方,内核空间是被动接受方。异步IO则反过来,是指系统内核是主动发起IO请求的一方,用户空间的线程是被动接受方.

 example:

   在Java应用程序进程中,默认情况下,所有的socket连接的IO操作都是同步阻塞IO(Blocking IO)。在阻塞式IO模型中Java应用程序从IO系统调用开始,直到系统调用返回,在这段时间内,Java进程是阻塞的。返回成功后,应用进程开始处理用户空间的缓存区数据。同步阻塞IO的具体流程,如图2-2所示。

举个例子,在Java中发起一个socket的read读操作的系统调用,

流程大致如下:

(1)从Java启动IO读的read系统调用开始,

用户线程就进入阻塞状态。

(2)当系统内核收到read系统调用,就开始准备数据。一开始,数据可能还没有到达内核缓冲区(例如,还没有收到一个完整的socket数据包),这个时候内核就要等待。

(3)内核一直等到完整的数据到达,就会将数据从内核缓冲区复制到用户缓冲区(用户空间的内存),然后内核返回结果(例如返回复制到用户缓冲区中的字节数)。

(4)直到内核返回后,用户线程才会解除阻塞的状态,重新运行起来。总之,阻塞IO的特点是:在内核进行IO执行的两个阶段,用户线程都被阻塞了。

阻塞IO的优点是:应用的程序开发非常简单;在阻塞等待数据期间,用户线程挂起。在阻塞期间,用户线程基本不会占用CPU资源。阻塞IO的缺点是:一般情况下,会为每个连接配备一个独立的线程;反过来说,就是一个线程维护一个连接的IO操作。在并发量小的情况下,这样做没有什么问题。但是,当在高并发的应用场景下,需要大量的线程来维护大量的网络连接,内存、线程切换开销会非常巨大。因此,基本上阻塞IO模型在高并发应用场景下是不可用的

2.同步非阻塞IO(Non-blocking IO)

非阻塞IO,指的是用户空间的程序不需要等待内核IO操作彻底完成,可以立即返回用户空间执行用户的操作,即处于非阻塞的状态,与此同时内核会立即返回给用户一个状态值。简单来说:阻塞是指用户空间(调用线程)一直在等待,而不能干别的事情;非阻塞是指用户空间(调用线程)拿到内核返回的状态值就返回自己的空间,IO操作可以干就干,不可以干,就去干别的事情。非阻塞IO要求socket被设置为NONBLOCK。强调一下,这里所说的NIO(同步非阻塞IO)模型,并非Java的NIO(New IO)库。

example:

socket连接默认是阻塞模式,在Linux系统下,可以通过设置将socket变成为非阻塞的模式(Non-Blocking)。使用非阻塞模式的IO读写,叫作同步非阻塞IO(None BlockingIO),简称为NIO模式。在NIO模型中,应用程序一旦开始IO系统调用,会出现以下两种情况:

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

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

同步非阻塞IO的流程,如图2-3所示。

举个例子。发起一个非阻塞socket的read读操作的系统调用,流程如下:

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

(2)内核数据到达后,用户线程发起系统调用,用户线程阻塞。内核开始复制数据,它会将数据从内核缓冲区复制到用户缓冲区(用户空间的内存),然后内核返回结果(例如返回复制到的用户缓冲区的字节数)。

(3)用户线程读到数据后,才会解除阻塞状态,重新运行起来。也就是说,用户进程需要经过多次的尝试,才能保证最终真正读到数据,而后继续执行。同步非阻塞IO的特点:应用程序的线程需要不断地进行IO系统调用,轮询数据是否已经准备好,如果没有准备好,就继续轮询,直到完成IO系统调用为止。同步非阻塞IO的优点:每次发起的IO系统调用,在内核等待数据过程中可以立即返回。用户线程不会阻塞,实时性较好。同步非阻塞IO的缺点:不断地轮询内核,这将占用大量的CPU时间,效率低下。总体来说,在高并发应用场景下,同步非阻塞IO也是不可用的。一般Web服务器不使用这种IO模型。这种IO模型一般很少直接使用,而是在其他IO模型中使用非阻塞IO这一特性。在Java的实际开发中,也不会涉及这种IO模型。这里说明一下,同步非阻塞IO,可以简称为NIO,但是,它不是Java中的NIO,虽然它们的英文缩写一样,希望大家不要混淆。Java的NIO(NewIO),对应的不是四种基础IO模型中的NIO(NoneBlockingIO)模型,而是另外的一种模型,叫作IO多路复用模型(IOMultiplexing)。

3.IO多路复用(IO Multiplexing)

即经典的Reactor反应器设计模式,有时也称为异步阻塞IO,Java中的Selector选择器和Linux中的epoll都是这种模型。

如何避免同步非阻塞IO模型中轮询等待的问题呢?这就是IO多路复用模型。在IO多路复用模型中,引入了一种新的系统调用,查询IO的就绪状态。在Linux系统中,对应的系统调用为select/epoll系统调用。通过该系统调用,一个进程可以监视多个文件描述符,一旦某个描述符就绪(一般是内核缓冲区可读/可写),内核能够将就绪的状态返回给应用程序。随后,应用程序根据就绪的状态,进行相应的IO系统调用。目前支持IO多路复用的系统调用,有select、epoll等等。select系统调用,几乎在所有的操作系统上都有支持,具有良好的跨平台特性。epoll是在Linux2.6内核中提出的,是select系统调用的Linux增强版本。在IO多路复用模型中通过select/epoll系统调用,单个应用程序的线程,可以不断地轮询成百上千的socket连接,当某个或者某些socket网络连接有IO就绪的状态,就返回对应的可以执行的读写操作。举个例子来说明IO多路复用模型的流程。发起一个多路复用IO的read读操作的系统调用,流程如下:

(1)选择器注册。在这种模式中,首先,将需要read操作的目标socket网络连接,提前注册到select/epoll选择器中,Java中对应的选择器类是Selector类。然后,才可以开启整个IO多路复用模型的轮询流程。

(2)就绪状态的轮询。通过选择器的查询方法,查询注册过的所有socket连接的就绪状态。通过查询的系统调用,内核会返回一个就绪的socket列表。当任何一个注册过的socket中的数据准备好了,内核缓冲区有数据(就绪)了,内核就将该socket加入到就绪的列表中。当用户进程调用了select查询方法,那么整个线程会被阻塞掉。

(3)用户线程获得了就绪状态的列表后,根据其中的socket连接,发起read系统调用,用户线程阻塞。内核开始复制数据,将数据从内核缓冲区复制到用户缓冲区。

(4)复制完成后,内核返回结果,用户线程才会解除阻塞的状态,用户线程读取到了数据,继续执行。

IO多路复用模型的特点:IO多路复用模型的IO涉及两种系统调用(SystemCall),另一种是select/epoll(就绪查询),一种是IO操作。IO多路复用模型建立在操作系统的基础设施之上,即操作系统的内核必须能够提供多路分离的系统调用select/epoll。和NIO模型相似,多路复用IO也需要轮询。负责select/epoll状态查询调用的线程,需要不断地进行select/epoll轮询,查找出达到IO操作就绪的socket连接。IO多路复用模型与同步非阻塞IO模型是有密切关系的。对于注册在选择器上的每一个可以查询的socket连接,一般都设置成为同步非阻塞模型。仅是这一点,对于用户程序而言是无感知的。IO多路复用模型的优点:与一个线程维护一个连接的阻塞IO模式相比,使用select/epoll的最大优势在于,一个选择器查询线程可以同时处理成千上万个连接(Connection)。系统不必创建大量的线程,也不必维护这些线程,从而大大减小了系统的开销。Java语言的NIO(NewIO)技术,使用的就是IO多路复用模型。在Linux系统上,使用的是epoll系统调用。IO多路复用模型的缺点:本质上,select/epoll系统调用是阻塞式的,属于同步IO。都需要在读写事件就绪后,由系统调用本身负责进行读写,也就是说这个读写过程是阻塞的。如何彻底地解除线程的阻塞,就必须使用异步IO模型。

4.异步IO(Asynchronous IO)

异步IO,指的是用户空间与内核空间的调用方式反过来。用户空间的线程变成被动接受者,而内核空间成了主动调用者。这有点类似于Java中比较典型的回调模式,用户空间的线程向内核空间注册了各种IO事件的回调函数,由内核去主动调用.

异步IO模型(AsynchronousIO,简称为AIO)。AIO的基本流程是:用户线程通过系统调用,向内核注册某个IO操作。内核在整个IO操作(包括数据准备、数据复制)完成后,通知用户程序,用户执行后续的业务操作。在异步IO模型中,在整个内核的数据处理过程中,包括内核将数据从网络物理设备(网卡)读取到内核缓冲区、将内核缓冲区的数据复制到用户缓冲区,用户程序都不需要阻塞.

举个例子。发起一个异步IO的read读操作的系统调用,流程如下:

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

(2)内核就开始了IO的第一个阶段:准备数据。等到数据准备好了,内核就会将数据从内核缓冲区复制到用户缓冲区(用户空间的内存)。

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

(4)用户线程读取用户缓冲区的数据,完成后续的业务操作。异步IO模型的特点:在内核等待数据和复制数据的两个阶段,用户线程都不是阻塞的。用户线程需要接收内核的IO操作完成的事件,或者用户线程需要注册一个IO操作完成的回调函数。正因为如此,异步IO有的时候也被称为信号驱动IO。异步IO异步模型的缺点:应用程序仅需要进行事件的注册与接收,其余的工作都留给了操作系统,也就是说,需要底层内核提供支持。理论上来说,异步IO是真正的异步输入输出,它的吞吐量高于IO多路复用模型的吞吐量。就目前而言,Windows系统下通过IOCP实现了真正的异步IO。而在Linux系统下,异步IO模型在2.6版本才引入,目前并不完善,其底层实现仍使用epoll,与IO多路复用相同,因此在性能上没有明显的优势。大多数的高并发服务器端的程序,一般都是基于Linux系统的。因而,目前这类高并发网络应用程序的开发,大多采用IO多路复用模型。大名鼎鼎的Netty框架,使用的就是IO多路复用模型,而不是异步IO模型。

本资料收集于Netty、Redis、Zookeeper高并发实战,希望大家购买正版图书地址:

关于java中常见io模型和Javaio模型的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。

The End

发布于:2022-11-23,除非注明,否则均为首码项目网原创文章,转载请注明出处。