四种主要IO模型
IO读写依赖于底层的read和write两大系统的调用。
read系统调用是把数据从内核缓冲区复制到进程缓冲区,write系统调用是把数据从进程缓冲区复制到内核缓冲区。内核缓冲区的数据写入到磁盘是由操作系统内核完成,底层操作系统会对内核缓冲区进行监控,等待缓冲区达到一定数量的时候,在进行IO设备的中断处理,几种执行物理设备的时机IO操作,至于什么时候中断(读、写中断)则有系统内核来决定。
java服务端完成一次socket的请求和响应过程:
1、linux通过网卡读取客户端的请求数据,将数据读取到内核缓冲区
2、java服务器通过read系统调用,从linux内核缓冲区读取数据,再送入java进程缓冲区
3、java服务器在进程缓冲区处理客户端请求
阻塞IO:需要内核IO操作完成后,才返回到用户空间执行用户的操作。
非阻塞IO:用户空间的程序不需要等待内核IOP操作完成,可以立即返回用户空间执行用户的操作,即处于非阻塞状态,与此同时,内核空间会立即返回给用户一个状态值
同步IO:是一种用户空间与内核空间的IO发起方式,同步IO指用户空间的线程主动向内核空间发起请求,异步IO则相反。
一、同步阻塞IO(Blocking IO)
由用户空间向内核空间主动发起请求,在内核空间没有彻底完成之前,用户空间不能做其他事情,只能等着。java程序从IO调用开始,知道系统调用返回,在这段时间内,java进程是阻塞的。返回成功后,应用程序开始处理用户空间的缓存区数据。
比如java发起一个sokcer的read系统调用:
1、从java启动IO读的read系统调用开始,用户线程就处于阻塞状态
2、当系统内核收到read系统调用,就开始准备数据。一开始,数据可能还没达到内核缓冲区,此时内核就要等待。
3、内核一直等到完成的数据到达,将数据从内核缓冲区复制到用户缓冲区,然后内核返回结果
4、内核返回后,用户线程才会接触阻塞状态,重新运行。
在内核进行的IO执行的两个阶段,都处于阻塞状态。
二、同步非阻塞IO(Non-blocking IO)
用户空间主动向内核空间发送请求,内核空间没有完成从网卡读取数据时,返回给用户空间一个标志,用户空间继续做其他事情,不会一直等着内核空间彻底完成,待内核空间读取数据完成后,用户空间才可以继续后面的事情
java应用程序开始IO系统调用,会出现一下两种情况:
1、在内核缓冲区中没有数据情况下,系统调用会立即返回一个调用失败的信息
2、在内核缓冲区中有数据情况下,是阻塞的,知道数据从内核缓冲区复制到用户缓冲区。复制完成后,系统调用返回成功,应用程序开始处理用户空间的缓存数据。
比如java发起一个sokcet的read系统调用
1、在内核数据没有准备好的阶段,用户线程发起IO请求时,立即返回。所以为了读取到最终数据,用户线程需要不断地发起IO系统调用
2、内核数据到达后,用户线程发起系统调用,被阻塞,内核开始复制数据,将数据从内核缓冲区复制到用户缓冲区,然后返回结果(例如返回复制到用户缓冲区的字节数)。
3、用户线程读到数据后,才会解除阻塞状态,重新运行起来。
这里的非阻塞只是指的是在内核空间还没准备好数据时的非阻塞,内核空间准备好数据,复制时也需要阻塞?。
三、IO多路复用(IO Multiplexing)
reactor反应器设计模式,有时也成为了异步阻塞IO(异步可以理解,阻塞不太理解?),java中的Selector和linux中的epoll都是这种模型。
通过该系统调用,一个进程可以监视多个文件描述符,一旦某个描述符就绪(一般是内核缓冲区可读或者可写),内核能够将就绪的状态返回给应用程序。随后,应用程序根据就绪的状态进行响应的IO系统调用。
目前支持IO多路复用的系统调用有select,epoll等,epoll是在linux 2.6内核中提出,是select系统调用的增强版本。
举例:
1、选择器注册。将需要read操作的木白哦sokcer网络连接,注册到select/epoll选择器中
2、轮询就绪的socket。查询注册过的所欲偶socket连接的就绪状态,通过查询的系统调用,内核返回一个就绪的socket列表,当注册过的socket中的数据准备好,内核缓冲区有数据(就绪),内核就将该socket加入到就绪的列表中,当用户进程调用了select方法,整个线程就会被阻塞
3、用户线程获取到就绪状态的列表后,分别针对每个socket发起read系统调用,用户线程阻塞,内核开始复制数据,将数据从内核缓冲区复制到用户缓冲区。
4、复制完成后,内核返回结果,,用户线程才会解除阻塞状态。
IO多路复用涉及到两种系统调用:select/epoll和IO操作(read/write)。
IO多路复用也需要轮询,负责select/epoll状态查询调用的线程,需要不断地进行select/epoll轮询,查找出达到IO操作就绪的socket。
java NIO(NEW IO),使用的就是IO多路复用模型。在linux系统使用的为epoll
四、异步IO(Asynchronous IO)
指用户空间线程变为被动接收者,内核空间称为主动调用者。用户空间线程向内核空间注册了各种IO时间的回调函数,由内核在指定的条件下主动调用
用户线程通过不系统调用,向内核注册某个IO操作,内核在整个IO操作(数据的准备和复制)完成后,通知用户程序,用户执行后续的业务处理。在异步IO模型只能够,整个内核的数据处理过程,包括内核将数据从网络物理设备读取到内核缓冲区以及将内核缓冲区数据复制到用户缓冲区,用户程序都不需要阻塞。
举例:
1、用户线程发起一各read系统调用,立刻就可以去做其他事情,不需要阻塞
2、内核开始准备数据(从网卡接收数据,放入内核缓冲区),准备好后,将数据从内核缓冲区复制到用户缓冲区。
3、内核会给用户线程发送一个信号,或者回调用户线程注册的回调接口,告诉用户线程read操作完成
4、用户线程读取到用户缓冲区数据,进行后面的业务处理
netty使用的为IO多路复用模型,而不是异步IO模型。