多路复用
目前支持I/O多路复用的系统调用有 select,pselect,poll,epoll,I/O多路复用就是通过一种机制,一个进程可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但select,pselect,poll,epoll本质上都是同步I/O
1. Linux 五种IO模型
io两阶段
- 数据准备
- 数据从内核复制到用户空间
io模型
3. io 同步阻塞,(买奶茶 -》 等待 -》 逛街)
4. nio 同步非阻塞,通过轮询(买奶茶 -》 逛街时不断轮询服务员是否做完)
5. 多路复用 select,poll,epoll,reactor(同步阻塞,服务端单个线程处理多个请求,买奶茶-》用户等待轮询大屏幕看是否做好)
线程一次 select 调用可以获取内核态中多个数据通道的数据状态。其中,select只负责等,recvfrom只负责拷贝,阻塞IO中可以对多个文件描述符进行阻塞监听,是一种非常高效的 I/O 模型。
- 信号驱动io(买奶茶-》逛街-》电话通知-》自己去拿)
- aio,异步io(点外卖-》玩-》送到家)
2.多路复用之select、poll、epoll
等待多个连接,能实现同时对多个IO端口进行监听
epoll原理
快速从内核中获取到等待处理的事件,这几个都是网络事件收集器的模型,select和poll是判断所有的链接,判断哪些链接有事件进来了,扫描了大量不活跃的链接,epoll通过两个数据结构,链表(存储准备好的事件)和红黑树(存储网络连接channel的读写事件,未就绪的事件),从而提高效率
- select()
本质上是通过设置或者检查存放fd标志位的数据结构来进行下一步处理,扫描时是线性扫描,即采用轮询的方法,效率较低
缺点:- 单个进程所打开的FD是有一定限制的;socket进行扫描时是线性扫描,效率低
- 维护一个用来存放大量fd的数据结构,这样会使得用户空间和内核空间在传递该结构时复制开销大
- poll
- 与select类似,使用链表存储fd,没有最大连接数限制
- 大量的fd的数组被整体复制于用户态和内核地址空间之间
- epoll
- callback而不是轮询的方式,不会随着FD数目的增加效率下降。只有活跃可用的FD才会调用callback函数;
- 没有最大并发连接的限制
- epoll通过内核与用户空间共享内存减少复制次数
select
基本原理
select 函数监视的文件描述符(通道的就绪状态)分3类,分别是writefds、readfds、和exceptfds。调用后select函数会阻塞,直到有描述符就绪(有数据 可读、可写、或者有except),或者超时(timeout指定等待时间,如果立即返回设为null即可),函数返回。当select函数返回后,可以通过遍历fdset,来找到就绪的描述符。
3. nio
- java selector事件选择,单线程管理多个通道,Selector.open() 方法创建一个 Selector 对象
- Channel 可以被注册到 Selector 对象上,在注册时候,需要指定通道的那些操作,是 Selector 感兴趣的。
- Channel.register(Selector sel, int pos) 方法,将一个通道注册到一个选择器
- 选择键,就绪状态通过Selector 的 select() 方法完成(依次询问每个通道是否已经就绪,效率不高),一旦通道有操作的就绪状态达成,并且是 Selecor 感兴趣的操作,就会被 Selector 选中,放入选择键集合中。 selector.selectedKeys();包含有channel和selector的注册关系和感兴趣的操作,类似事件驱动的event,只是是主动查询而不是被动的驱动
使用方式:
// 1. 获取 Selector 选择器
Selector selector = Selector.open();
// 2. 获取通道
ServerSocketChannel socketChannel = ServerSocketChannel.open();
// 3. 设置为非阻塞
socketChannel.configureBlocking(false);
// 4. 绑定连接
socketChannel.bind(new InetSocketAddress(9999));
// 5. 将通道注册到选择器
socketChannel.register(selector, SelectionKey.OP_ACCEPT);
// 6调用 Selector 中的 select 方法(循环调用),监听通道是否是就绪状态
// 7调用 SelectKeys() 方法就能获取 就绪 channel 集合
// 8遍历就绪的 channel 集合,判断就绪事件类型,实现具体的业务操作
4. Reactor 模式
有Reactor监听网络连接请求,通过Acceptor(IO线程池),转发(轮训)给io线程,io线程通过select监听读写事件,转发给业务线程处理