一、IO的过程
一次IO请求存在2个阶段
阶段一:等待数据,即数据从I/O设备到内核内存(操作系统缓冲区)。(I/O设备可能为磁盘, 也可能为网卡)
阶段二:复制数据,即数据内核内存到进程内存(应用程序缓冲区)
阻塞/非阻塞,同步/非同步
阶段一:阻塞/非阻塞 【DMA Copy】
阶段二:同步/非同步 【CPU Copy】
二、I/O模型
(面试题)谈谈I/O模型?
1、首先I/O请求过程,指的是数据从IO设备拷贝到用户空间的步骤(两步,等待数据和复制数据阶段)
2、每种IO模型对这两步的处理不同,像阻塞I/O模型,它对于这两个阶段都是阻塞的 ,非阻塞I/O模型,对于等待数据阶段会对一个文件描述符不断发起检测,并会立即返回一个结果,如果可以,就阻塞进行复制数据阶段,I/O复用模型在等待数据阶段会阻塞,来检查多个文件描述符,不会立即返回结果,只要有一个文件描述符可以工作,就阻塞进行复制数据阶段,信号驱动I/O模型在等待数据阶段会发送一个信号,并等待通知,然后阻塞完成复制数据阶段,异步I/O模型这两个阶段都是非阻塞的。
三、I/O多路复用
I/O多路复用运行进程同时检查多个文件描述符来找出他们中的任何一个是否可执行I/O操作
三种实现方式:
Select
Poll
Epoll
Select:通过设置或检查存放fd标志位的数据结构来进行下一步处理
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)
缺点:
1、重复的CPU拷贝:每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大
2、存在无效的遍历:同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时会很大
3、单线程select支持的文件描述符受限于FD_SETSIZE宏(即c的常量定义),是操作系统定义的,Linux默认是1024
Poll:本质与Select无区别,只是不受FD_SETSIZE宏限制。
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
优点:采用链表的方式存储pollfd,不受FD_SETSIZE限制,只受限于操作系统允许打开的最大文件描述符数目
缺点:
1、每次调用poll,都需要把pollfd链表从用户态拷贝到内核态,这个开销在fd很多时会很大
2、同时每次调用poll都需要在内核遍历传递进来的所有fd,这个开销在fd很多时会很大
Epoll:Event Poll,使用一个文件描述符管理多个描述符,将用户关心的文件描述符事件放入内核到一个事件表中。
int epoll_create(int size):创建epoll文件描述符
size:需要监听的数目一共有多大。
返回值:一个文件描述符(epfd),用于epoll_ctl、epoll_wait
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event):管理epoll监听的文件描述符与事件类型
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout):收集在epoll监控的事件中已发生的事件
优点:
1、epoll会在epoll_ctl()中注册,只需要将所有的fd拷贝到内核事件表一次,不用再每次调用epoll_wait()时重复拷贝
2、epoll只返回就绪的描述符,减少无效的遍历
3、能支持的fd数量是单个进程最大打开文件数
缺点:编程复杂
(面试题)什么是I/O多路复用?
1、I/O多路复用是I/O请求的一种模型
2、它有三种实现方式,分别是select、poll以及epoll,对于select,它是通过设置和检查存放fd标志位的数据结果来进行下一步处理,它的缺点是重复的CPU拷贝,存在无效的遍历、单线程select支持的文件描述符还受限制(宏限制,操作系统定义的),poll利用链表来存储fd集合,使得单线程支持的文件描述符不再受限于宏,但是它的缺点仍然包括重复的CPU拷贝和存在无效的遍历,而epoll是通过使用一个文件描述符来管理多个文件描述符,将用户关心的文件描述符事件放入内核事件表,所以不会重复的CPU拷贝,同时它只返回就绪的描述符,不会有无效的遍历,支持的fd数量是单个进程能打开的最大文件数。
四、Java IO模型
三种IO模型:
1、OIO:Old IO,也可以叫BIO(Blocking-IO),同步阻塞IO
2、NIO:New IO,也可以叫NIO(None-Blocking IO),同步非阻塞IO 【JDK 1.4+】
3、AIO:异步非阻塞IO 【JDK1.7+】
‘
BIO对应 Linux IO模型的阻塞IO
’NIO和AIO对应Linux IO模型的IO多路复用,并且都是使用epoll实现
OIO
1、抽象模型可以分为:流、输入输出流、字符字节流,就是InputStream、OutStream、Reader、Writer
2、ServerSocket和Socket程序调用会阻塞,所以也将此类归结于BIO中。
NIO
1、抽象模型可以分为:Channel管理、Buffer缓冲、Selector选择器
Channel与Buffer:数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。
Selector:允许单线程处理多个 Channel

OIO和NIO的区别
NIO的Buffer:一块可以写入数据,也可以从中读取数据的内存对象。
运用到了适配器模式、装饰器模式
基本属性:
容量(capacity):缓冲区能够容纳元素的最大数量
上界(limit):缓冲区实际容纳元素的数量
位置(position):下一个要被读写的元素的数组下标索引
buffer.flip():Buffer读写模型的切换 limit设置为position当前的值;postion会被置为0。
Selector:选择器,多路复用器,能够检测一到多个NIO通道,并能够知晓通道是否为诸如读写事件做好准备的组件。
过程:
1、创建Selector
Selector selector = Selector.open();
2、向Slector注册通道【FileChannel不可以注册】
channel.configureBlocking(false);
SelectionKey key = channel.register(selector, Selectionkey.感兴趣的事件);
3、开始工作,阻塞到至少有一个通道在你注册的事件上就绪
selector.select()