I/O 模型
数据的读取分为两个阶段:
- 准备数据
- 将数据从内核拷贝到进程(用户空间)
对于套接字的输入操作,第一步通常是等待数据从网络中到达,当所有分组到达时,将它复制到内核的某个缓冲区(准备数据);第二步是把数据从内核缓冲区复制到应用进程缓冲区
5种I/O 模型
阻塞式I/O
blocking i/o
非阻塞式I/O
采用轮训式,一旦内核中数据准备好,便等待数据复制后返回
nonblocking i/o
I/O复用
分为两步,采用select(poll)
函数(阻塞),等待数据报套接字变成可读。当select
返回这一条件后,再调用 recvfrom
函数去读取数据
i/o multiplexing
信号驱动式I/O
信号驱动式
异步I/O
对比
前四种在第二阶段相似,都在等待数据从内核复制到用户进程那里阻塞
5种模型的对比
I/O
复用
select
函数
1 | int select(int maxfd1, fd_set * __restrict, fd_set * __restrict, |
struct timeval
1 | _STRUCT_TIMEVAL |
告知内核等待所制定描述符中的任何一个就绪可花费多长时间
这个参数有三种可能:
- 永远等待下去,有一个描述符准备好后I/O才返回,此时将该
__restrict
设置为 null - 等待一段固定时间, 有一个描述符准备好后I/O才返回, 等待固定时间(
__restrict
)未收到也直接返回 - 不等待, 检查描述符后立刻返回,类似于
轮询
的方式__restrict
设置为0
其实第1,3都是第2中的一种特例,区分在于时间的设置
中间三个fd_set
代表 可读、可写、发生异常三种描述符,都是整数数组。在select
函数中可以置为空
异常分为两种
- 某个套接字的外带数据到达
- 某个已经设置为分组模式的伪终端存在可从其主端读取的控制状态信息
fd_set
是一个整数数组
,最大可以表示1024个描述符数量,它底层是一个整数数组(int型数组,大小为32个)
1 |
|
操作fd_set
主要使用以下宏
1 |
|
判定条件
maxfd1
带测试描述符的个数,通常为最大描述符+1
因为,它扫描 0~maxfd1
区间的描述符
poll
函数
1 | int poll(struct pollfd *, nfds_t, int) |
从函数可以看出,它于select
的最大区别在于,它将fd组织成了一个链表,这样突破了1024的限制,理论上是无限的。它将三种事件描述符(可读、可写、异常发生)绑定到每个fd上,
1 | struct pollfd { |
他将事件分为以下几种,通过判定revents字段来确认是否准备完毕
select | poll | |
---|---|---|
fd 限制 | 有 FD_SETSIZE (1024/2048) | 无 |
获取就绪的监听时间 | O(N) | O(N) |
实现结构 | fd_set 三组 | struct pollfd *__fds |
epoll
函数
上面的 select 和 poll 函数均存在各自的问题。
fd
限制: select 存在 FD_SETSIZE,1024
- 执行时间伴随着监听的
fd
增多而线性增加: 内核中实现select
是用轮询方法,即每次检测都会遍历所有FD_SET中的句柄 - 多次内核拷贝
epoll
函数的优点:
支持一个进程打开大数目的socket描述符(FD)
epoll
所支持的FD上限是最大可以打开文件的数目,这个数字一般远大于2048
,举个例子,在1GB
内存的机器上大约是10
万左 右,具体数目可以cat /proc/sys/fs/file-max
察看,一般来说这个数目和系统内存关系很大。IO 效率不随FD数目增加而线性下降
在内核实现中epoll是根据每个`fd`上面的`callback`函数实现的,只有"活跃"的`socket`才会主动的去调用 `callback`函数,其他`idle`状态`socket`则不会
使用mmap加速内核 与用户空间的消息传递。
无论是`select`,`poll`还是`epoll`都需要内核把`FD`消息通知给用户空间,如何避免不必要的内存拷贝就 很重要,在这点上,`epoll`是通过内核与用户空间`mmap`同一块内存实现的。
epoll 分为三种函数
1 | // 创建一个 epfd |
一种典型的用法
参考 [github]
1 | /* |
References
- 本文标题:I/O 复用select\poll\epoll 初探
- 本文作者:codeflysafe
- 创建时间:2022-03-04 22:42:05
- 本文链接:https://codeflysafe.github.io/2022/03/04/io复用/
- 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!