Version: Next
I/O 多路复用
思路递进:
- 什么是 BIO、什么是 NIO (同步阻塞、同步非阻塞;BIO 面向流,NIO 面向 Buffer 或者说 Channel)
- BIO 的缺陷(没连接、没数据都阻塞)、NIO 如何试图解决 BIO 的缺陷,简单的 NIO 有何不足(不停的轮询所有连接、不管有没有数据;读写操作引发用户态、内核态切换)
- NIO + 多路复用器(socket 在 linux 中有对应的
文件描述符 fd
,多路复用器用来一次获知哪些 fd 可写可读哪些不能)- Linux 多路复用器的三种实现 select、poll、epoll、原理、各自优缺点
- Select:用户态:将
已连接
socket 的 fd 集合拷贝到内核态,内核遍历 fd 集合标记可读可写的 socket fd(一次传输一次遍历);把 fd 集合传输会用户态,用户态再遍历 fd 集合对可读可写的 fd 进行读写操作(再次传输和再次遍历);fd 集合的长度固定1024 bit
- Poll:传输 fd 没有数量限制,基于链表的动态数组,LinkedArrayList
- Select、poll 都要遍历 fd 集合,fd 集合需要在用户态、核心态之间传递
- Epoll 如何解决 select、poll 的不足
- 数据到达网卡,积攒一定量,触发系统中断,通过 DMA 技术复制到内存,和相关 fd 绑定
- Epoll 中的三个数据结构
- 在内核中维护一颗 fd
红黑树
- 从网卡到达数据相应的 fd 移动到
就绪队列 readyList
,是一个双向链表
(只在内核态操作)等待队列
里面是调用了 epoll_wait 函数的进程(当 socket 有事件发生时触发),epoll_wait
时将能读到的 fd 从 readyList 复制出去,返回给用户程序(内核态到用户态)epoll_create
用来初始化 epollepoll_ctl
把 fd 添加到红黑树(从用户态拷贝到内核态)- Epoll下:多个客户端连接 -> 多个 socket -> 多个 buffer -> 多个 Channel -> 一个 Selector -> 一个线程
- 水平触发:只要缓冲区还有内容,就返回读就绪:Select、Poll;边缘触发:只有缓冲区内容发生变化,才返回读就绪 Epoll
- Java 中的 Selector 对 select、poll、epoll 的封装
- Netty 是优秀的 NIO 框架
select | poll | epoll | |
---|---|---|---|
操作方式 | 遍历 | 遍历 | 回调 |
底层实现 | 数组 | 链表 | 红黑树 |
IO 效率 | 每次调用都进行线性遍历,时间复杂度 O(n) | 每次调用都进行线性遍历,时间复杂度 O(n) | 事件触发,每当 fd 就绪,系统注册的回调函数就会被调用,将就绪 fd 存放到 readyList 双向链表,时间复杂度 O(1) |
最大连接数 | 32位机 1024 64位机 2048 | 无上限 | 无上限 |
fd 拷贝 | 每次调用 select 都需要把 fd 集合从用户态拷贝到内核态,再拷贝回用户态 | 每次调用 poll 都需要把 fd 集合从用户态拷贝到内核态再拷贝回用户态 | 调用 epoll_ctl 时拷贝进内核并保存到红黑树,之后每次 epoll_wait 不拷贝,直接查红黑树 |
https://zhuanlan.zhihu.com/p/150635981
https://blog.csdn.net/weixin_44273302/article/details/115269745