Linux - 三组 I/O 复用函数的比较

12 篇文章 27 订阅

三组 I/O 复用函数的比较

  • select
  • poll
  • epoll

select

/* According to POSIX.1-2001 */
#include <sys/select.h>

/* According to earlier standards */
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

int select(int nfds, 
           fd_set *readfds, 
           fd_set *writefds,
           fd_set *exceptfds, 
           struct timeval *timeout);

void FD_CLR(int fd, fd_set *set);
int  FD_ISSET(int fd, fd_set *set);
void FD_SET(int fd, fd_set *set);
void FD_ZERO(fd_set *set);

poll

#include <poll.h>

int poll(struct pollfd *fds, 
         nfds_t nfds, 
         int timeout);

epoll

#include <sys/epoll.h>

// 返回一个文件描述符, 标识内核事件表
int epoll_create(int size);
// 操作内核事件表
int epoll_ctl(int epfd, // epoll_create 的返回值
              int op, // 操作类型: 注册 修改 删除
              int fd, // 要操作的文件描述符
              struct epoll_event *event); // 事件类型
// 在一段超时时间内等待一组文件描述符上的事件
// 成功返回就绪的文件描述符的个数, 失败返回-1
int epoll_wait(int epfd, 
               struct epoll_event *events,
               int maxevents, 
               int timeout);
LT & ET 模式

LT 模式是默认的工作模式 , 此模式下的 epoll 相当于一个效率较高的 poll

当在 epoll 内核事件表中注册一个文件描述符上的 EPOLLET 事件时 , epoll 将以 ET 模式来操作该文件描述符 , ET 模式是 epoll 的高效工作模式

对于采用 LT 工作模式的文件描述符 , 当 epoll_wait 检测到其上有事件发生并将此事件通知应用程序后 , 应用程序可以不立即处理该事件 , 当下一次调用 epoll_wait 时 , 该事件还会被再次通告

对于采用 ET 工作模式的文件描述符 , 应用程序必须立刻处理该事件 , 因为后续的 epoll_wait 调用将不再通知这一事件

ET 模式在很大程度上降低了同一个事件被重复触发的次数 , 因此效率要比 LT 模式高

EPOLLONESHOT 事件

在多进程或多线程中 , 如果一个进程(或线程)读取完某个 socket 上的数据后开始处理这些数据 , 在这期间该 socket 上又有新的数据可读 (EPOLLIN 再次被触发) , 这时另一个进程(线程)就会被唤醒来读取这些新的数据 , 此时就出现了两个进程(线程)同时操作一个 socket 的局面

为了解决这个问题 , 让一个 socket 在任意时刻都只被一个进程(线程)处理 , 可以使用 epoll 的 EPOLLONESHOT 事件实现

对于注册了 EPOLLONESHOT 事件的文件描述符 , 系统最多只触发一个其上注册的事件(可读事件, 可写事件, 异常事件) , 并且只触发一次 , 除非使用 epoll_ctl 函数重置该文件描述符上注册的 EPOLLONESHOT 事件

所以当 一个线程处理完注册了 EPOLLONESHOT 事件的socket的时候 , 该线程应该立即重置这个 socket 上的 EPOLLONESHOT 事件 , 以确保下一次这个 socket 可读时 , 其 EPOLLIN 事件能被触发 , 能让其他线程有机会继续处理这个 socket

epoll 工作原理

当某⼀进程调用 epoll_create 函数时,Linux内核会创建⼀个 eventpoll 结构体,这个结构体中有两个成员与 epoll 的使用方式密切相关

struct eventpoll { 
 ... ...
 /*红⿊树的根节点,这颗树中存储着所有添加到epoll中的需要监控的事件*/ 
 struct rb_root rbr; 
 /*双链表中则存放着将要通过epoll_wait返回给⽤户的满⾜条件的事件*/ 
 struct list_head rdlist; 
 ... ...
} ; 

每⼀个epoll对象都有⼀个独⽴的 eventpoll 结构体,⽤于存放通过 epoll_ctl 函数向epoll对象中添加进来的事件
这些事件都会挂载在红⿊树中,如此重复添加的事件就可以通过红⿊树⽽⾼效的识别出来(红⿊树的插⼊时间效率是O(logN),其中N为树的⾼度)
⽽所有添加到epoll中的事件都会与设备(网卡)驱动程序建⽴回调关系

也就是说 , 当响应的事件发⽣时会调⽤这个回调⽅法
这个回调⽅法在内核中叫 eppollcallback , 它会将发⽣的事件添加到 rdlist 双链表中
在epoll中,对于每⼀个事件,都会建⽴⼀个epitem结构体

struct epitem
{
    struct rb_node rbn;//红黑树节点
    struct list_head rdllink;//双向链表节点
    struct epoll_filefd ffd; //事件句柄信息
    struct eventpoll *ep; //指向其所属的 eventpoll 对象
    struct epoll_event event; //期待发生的事件类型
}

当调⽤ epoll_wait 检查是否有事件发⽣时,只需要检查 eventpoll 对象中的rdlist 双链表中是否有 epitem 元素即可

如果 rdlist 不为空,则把发⽣的事件复制到⽤户态,同时将事件数量返回给⽤户

这个操作的时间复杂度是O(1)

相同点

都能同时监听多个文件描述符

都是通过某种结构体变量来告诉内核监听哪些文件描述符上的哪些事件 , 并使用该结构体类型的参数来获取内核处理的结果

不同点

select 的参数类型 fd_set 没有将文件描述符和事件绑定 , 只是一个文件描述符集合 , 因此 select 需要提供 三个此类型的参数来分别 传入和输出 可读 , 可写 , 异常等事件 , 这使得 select 不能处理更多类型的事件 , 又因为内核对 fd_set 集合的在线修改 , 下次调用 select 之前必须重置 这 3 个 fd_set 集合

poll 的参数类型 pollfd 将文件描述符和事件绑定 , 任何事件都被统一处理 , 并且内核每次修改的是 pollfd 结构体的 revents 成员 , 而 events 成员保持不变 , 因此下次调用 poll 时无需重置 pollfd 类型的事件集参数

epoll 适用于连接数量多 , 但活动连接较少的情况 , 因为当活动连接较多时 , 回调函数的触发过于频繁 , 其效率未必高于 select 和 poll

系统调用selectpollepoll
事件集合用户通过 3 个fd_set类型的参数分别传入可读, 可写, 异常事件, 内核通过对这些参数的在线修改来反馈其中的就绪事件 . 这使得每次调用 select 都要重置这 3 个参数统一处理所有事件类型 , 因此只需要一个 事件集参数 . 用户通过 pollfd.events 传入事件 , 内核通过修改 pollfd.revents 反馈其中就绪的事件内核通过一个事件表直接管理所有事件 . 每次调用 epoll_wait无需反复传入用户事件 . epoll_wait 的参数 events 仅用来反馈就绪的事件
应用程序索引就绪文件描述符的时间复杂度O(n)O(n)O(1)
最大支持的文件描述符数量有最大限制6553565535
工作模式LT 模式LT 模式LT / ET (高效模式)
内核实现和工作效率采用轮训方式检测就绪事件 , 算法的时间复杂度为 O(n)采用轮训方式检测就绪事件 , 算法的时间复杂度为 O(n)采用回调机制来检测就绪事件 , 算法时间复杂度为 O(1)
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值