- select基于轮询机制
- epoll基于操作系统支持的I/O通知机制
对比
性能
- select: 随着连接数增加, 性能下降
- epoll: 随着连接数增加, 性能基本没有下降
连接数
- select: 连接数有限制, 处理的最大连接数不超过1024. 如超过1024个连接数, 需要修改FD_SETSIZE宏, 并重新编译, 但性能会受到影响(32位1024, 64位2048)
- epoll: 连接数无限制
内在处理机制
- select: 对socket线性扫描, 轮询, 效率低, O(n)
- epoll: 回调callback
poll
和select类似, 只是描述fd集合的方式不同, poll使用pollfd结构, 而非fd_set结构. 使用链表数据结构存储 没有最大连接数的限制.
缺点和select一样, 包含大量文件描述符的数组被整体复制与用户态和内核的敌地址空间之间.
- 它将用户传入的数组拷贝到内核空间
- 然后查询每个fd对应的设备状态:
-- 如果设备就绪 在设备等待队列中加入一项继续遍历
-- 若遍历完所有fd后,都没发现就绪的设备 挂起当前进程,直到设备就绪或主动超时,被唤醒后它又再次遍历fd。这个过程经历多次无意义的遍历。
epoll
修改主动轮询为被动通知, 当有事件发生时, 被动接收通知.
epoll模型注册套接字后, 主程序可以做其他事情, 当事件发生时, 接收到通知后再去处理.
epoll是事件驱动, 每个事件关联fd, 复杂度O(1)
触发模式(epollLT&epollET)
- LT: 默认模式(水平触发), 只要该fd还有数据可读, 每次epoll_wait都会返回他的事件, 提醒用户程序去操作
- ET: 高速模式(边缘触发), 字体是一次, 直到下次有数据流入之前不再提示看无论fd中是否还有数据可读. 所以在ET模式下, read一个fd的时候一定把buffer读完, 读到read返回值小于请求值或遇到EAGAIN错误.
优点
- 没有最大并发连接数的限制, 能打开的FD上线远大于1024
- 效率提升, 不是轮询, 不会随着FD的数据增加效率降低, 只有活跃可用的FD才会调用callback函数. EPoll最大的有点在于它只关心"活跃"的连接, 跟连接总数无关.
- 内存拷贝, 利用mmap()文件映射内存加速与内核空间的消息传递, epoll使用mmap减少复制开销
- epoll通过内核和用户空间共享一块内存来实现
select和poll都只提供一个函数 -- select或者poll函数.
epoll提供三个函数:
- epoll_create: 创建一个epoll句柄
- epoll_ctl: 注册要监听的事件类型
- epoll_wait: 等待事件发生