程序员求职经验分享与学习资料整理平台

网站首页 > 文章精选 正文

「干货」IO多路复用技术(select、poll、epoll模型)

balukai 2025-03-26 09:50:52 文章精选 9 ℃

目前常用的IO复用模型主要有三种:select/poll/epoll

首先这三种都是实现IO多路复用的方式;

IO多路复用是指内核一旦发现进程指定的一个或者多个IO条件准备读取,他就通知该进程。与多线程技术相比,IO多路复用最大的优势是系统开销小,系统不必创建核维护这些线程、进程,从而大大的减少系统的开销。

1.select模型

时间复杂度为O(n),有IO事件发生了,却不知道用哪个流,只能进行无差别的轮询所有的流,找出读或者写的流,再对他们进行操作。

缺点:

  • 每次调用slect,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大,呈现线性增长。
  • 同时每次调用select ,都需要任内核态遍历所有传递过来的fd,这个开销在fd很多时也很大。
  • select 支持的文件描述得太小,黑大认:1024/ 32位机器,单个进程能打开的最大连接数由FD_SetSize来定义。
  • select模型是水平触发,应用程序如果没有完成对一个已经就绪的文中描述符进行IO操作,那么之后每次进行Select调用还是会将这些文件描述符通知进程。

select模型流程步骤:

  • 创建文件描述符集合fd_set,可以关注上面的读、写、异常事件,要创建3个fd_set,分别对这三种事件进行监听收集。
  • 调用select等待事件的发生。
  • 轮询select所有fd_set中的每一个fd,检查是否有相应的事件发生,如果有,则进行处理。

Select函数:

int select(nfds, readfds, writefds, exceptfds, timeout);

nfds:select监视的文件句柄数,视进程中打开的文件数而定,一般设为你要监视各文件中的最大文件号加一。(注:nfds并非一定表示监视的文件句柄数。官方文档仅指出nfds is the highest-numbered file descriptor in any of the three sets, plus 1. (可在linux环境中通过man select命令查得))

readfds:select监视的可读文件句柄集合。

writefds: select监视的可写文件句柄集合。

exceptfds:select监视的异常文件句柄集合。

timeout:本次select()的超时结束时间。非阻塞模式下最大的等待事件。

2.poll模型

事件复杂度为O(n).poll的本质与select没有区别,他将用户传入的数据拷贝到内核空间,然后查询每个fd对应的设备状态,但是没有最大连接数的限制,原因是他是基于链表来存储。注意:windows平台不支持poll。

poll函数:int poll(Struct pollfd* fds,unsigned int nfds, int timeout)

pollfd结构体定义如下:

Struct Pollfd{ int fd; // 文件描述符

Short events;// 等待的事件

Short revents;// 实际发生的事件}

Poll模型流程步骤:

  • 创建描述符集合,设置关注的事件;
  • 调用poll()函数,等待事件的发生,类似select,poll也可以设置等待时间;
  • 轮询描述符事件,检查事件,处理事件;

select与poll的区别:

  • select需要为读、写、异常事件分别建立一个文件描述符集合,最后轮询的时候需要分别轮询这三个集合;而poll只需要创建一个文件描述符集合,在每个文件描述符对应的结构上分别设置读、写、异常事件,最后轮询的时候,同时检查这三个事件;
  • poll没有最大连接数的限制,原因是基于链表进行存储;

缺点:

  • 大量的fd复制于用户态与内核态之间,而不管这样的复制有没有意义;
  • 水平触发,如果报告了fd,没有被处理,那么下次poll时还会继续触发报告;

3.epoll模型

时间复杂度为O(1);

epoll模型流程步骤:

  • 通过调epoll-crete来创建一个epoll文件描述符(句柄)、epol-create中有一个整型的参数Size,用来设置描述符事件列表的大小。
  • epoll_create (int size);
  • 通过调用epoll_ctrl来给描述符设置关注的事件,并把他添加到内核的事件列表中;
  • epoll_ctrl(int epfd,int fd,struct epoll_event * event);
  • 通过调用epoll_wait来等待内核通知事件发生,进而进行事件的处理;
  • epoll_wait()

epoll的特点:

  • 可以说没有最大并发数的限制,能打开的上限远远大于1024(1G内存上能监听的端口约为10万个端口);
  • 效率提升:不是轮询的方式,不会随着fd数目的增加而效率下降,只有活跃的用户的FD才会调用callback函数,即epoll最大的优点在于只管活跃的连接数,而跟连接总数无关;
  • 内存拷贝:使用了零拷贝技术。epoo通过用户态与内核态共享一块内存,来实现消息的传递;利用mmap()文件映射内存加速内核空间的消息传递,即epoll使用mmap减少复制开销;
  • epoll保证了每个FD在整个拷贝过程中只拷贝一次,select、poll每次调用都要把FD集合从用户态拷贝到内核态一次。


最近发表
标签列表