io多路复用
io多路复用概念
IO多路复用是一种同步IO模型,一个线程监听多个IO事件,当有IO事件就绪时,就会通知线程去执行相应的读写操作,没有就绪事件时,就会阻塞交出cpu。多路是指网络链接,复用指的是复用同一线程。
select
- 用户线程调用select,将fd_set从用户空间拷贝到内核空间
- 内核在内核空间对fd_set遍历一遍,检查是否有就绪的socket描述符,如果没有的话,就会进入休眠,直到有就绪的socket描述符
- 内核返回select的结果给用户线程,即就绪的文件描述符数量
- 用户拿到就绪文件描述符数量后,再次对fd_set进行遍历,找出就绪的文件描述符
- 用户线程对就绪的文件描述符进行读写操作
优点
- 所有平台都支持,良好的跨平台性
缺点
- 每次调用select,都需要将fd_set从用户空间拷贝到内核空间,当fd很多时,这个开销很大
- 最大连接数(支持的最大文件描述符数量)有限制,一般为1024
- 每次有活跃的socket描述符时,都需要遍历一次fd_set,造成大量的时间开销,时间复杂度是O(n)
- 将fd_set从用户空间拷贝到内核空间,内核空间也需要对fd_set遍历一遍
poll
数据结构定义如下,链表存储
/* Data structure describing a polling request. */
struct pollfd
{
int fd; /* File descriptor to poll. */
short int events; /* Types of events poller cares about. */
short int revents; /* Types of events that actually occurred. */
};
与select的异同点
相同点:
(执行过程与select类似)
- 内核线程都需要遍历文件描述符,并且当内核返回就绪的文件描述符数量后,还需要遍历一次找出就绪的文件描述符
- 需要将文件描述符数组或链表从用户空间拷贝到内核空间
- 性能开销会随文件描述符的数量而线性增大
不同点:
- select存储的数据结构是文件描述符数组,poll采用链表
- select有最大连接数限制,poll没有最大限制,因为poll采用链表存储
epoll
epoll_create创建eventpoll对象(红黑树,双链表)
一棵红黑树,存储监听的所有文件描述符,并且通过epoll_ctl将文件描述符添加、删除到红黑树
一个双链表,存储就绪的文件描述符列表,epoll_wait调用时,检测此链表中是否有数据,有的话直接返回
所有添加到eventpoll中的事件都与设备驱动程序建立回调关系
缺点
- 只能工作在linux下
优点
- 时间复杂度为O(1),当有事件就绪时,epoll_wait只需要检测就绪链表中有没有数据,如果有的话就直接返回
- 不需要从用户空间到内核空间频繁拷贝文件描述符集合,使用了内存映射(mmap)技术
- 当有就绪事件发生时采用回调的形式通知用户线程
- 应用场景
- 连接数较少并且都很活跃,用select和poll效率更高
- 连接数较多并且都不很活跃,使用epoll效率更高
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!