select、poll、epoll之间的区别

本文详细对比了select、poll和epoll三种I/O多路复用技术,阐述了它们在不同场景下的优劣。select和poll采用轮询方式,而epoll采用触发式,效率更高,尤其在Linux2.6内核后的高性能网络服务器中,epoll成为关键技术。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

select、poll、epoll之间的区别

前言

在之前的博客中介绍了五种I/O模型(阻塞,非阻塞,多路复用,信号,异步)。https://blog.youkuaiyun.com/lyn_00/article/details/84780288
select、poll和epoll是三种实现I/O的机制,本质上他们都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的。
下面我们来深入探讨他们。

select

select函数监视的文件描述符分3类,分别是writefds、readfds、和exceptfds。

原理:
进程调用select函数会被阻塞,然后内核会轮询检查select负责的fd,直到有文件描述符就绪(或超时timeout),select函数就会返回。若未超时,当select函数返回后,可以通过调用FD_ISSET,遍历fdset,来找到就绪的描述符后进程被唤醒,处理结束后又继续轮询检查。

FD_ZERO(fd_set *fdset);              //将set清零使集合中不含任何fd
FD_SET(int fd, fd_set *fdset);       //将fd加入set集合
FD_CLR(int fd, fd_set *fdset);       //将fd从set集合中清除
FD_ISSET(int fd, fd_set *fdset);     //检测fd是否在set集合中,不在则返回0
//成功时返回fd的总数,错误时返回SOCKET_ERROR
int select(int nfds,  fd_set* readset,  fd_set* writeset,  fe_set* exceptset,  struct timeval* timeout);

参数:

  • nfds 需要检查的文件描述字个数
  • readset 用来检查可读性的一组文件描述字。
  • writeset 用来检查可写性的一组文件描述字。
  • exceptset 用来检查是否有异常条件出现的文件描述字。(注:错误不包括在异常条件之内)
  • timeout 超时,填NULL为阻塞,填0为非阻塞,其他为一段超时时间

如下如所示:
在这里插入图片描述

select 的缺点

select本质上是通过设置或者轮询检查存放fd标志位的数据结构(fdset)来进行下一步处理,这样做带来了一些缺点:

  • 单个进程可监视的fd数量是有限的。
    一般来说这个数目和系统内存关系很大,具体数目可以cat /proc/sys/fs/file-max察看。
    32位机默认是1024个。64位机默认是2048。
  • 采用轮询的方法,效率较低。
    每一次调用select都需要将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态。
    当监听的fd数量较多但活跃数(就绪)较少时,select仍然需要遍历检查FD_SETSIZE个fd,他的时间复杂度为O(n)。
  • 需要维护一个用来存放大量fd的数据结构,这样会使得用户空间和内核空间在传递该结构时复制开销大
poll

poll有一个“水平触发”的特点,如果报告了fd后,没有被处理,那么下次poll时会再次报告该fd。

pollselect在本质上没有什么区别,但他解决了select对监听数量有限制这一缺点,在使用poll时,可以传入一个自定义大小的结构体数组,不再受限制。但仍存在问题。

  • 仍然采用轮询的方式进行fd检查,每次调用都会将数组传入内核空间,不论其是否有意义
epoll

在linux上,2.4内核前主要是selectpoll,自Linux 2.6内核正式引入epoll以来,epoll已经成为了目前实现高性能网络服务器的必备技术。

与前两种方式相比,epoll并不是采用轮询的方式监听事件,而是触发式。
我们先了解一下epoll的函数再讲解它的内部机制。

epoll的三个函数:

新建一个epoll对象

//返回一个fd
int epoll_create(int size);

添加、删除或修改所有待监控的连接

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

参数:

  • epfd :epoll_create()的返回值
  • op:表示动作,用三个宏来表示:
    EPOLL_CTL_ADD:注册新的fd到epfd中;
    EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
    EPOLL_CTL_DEL:从epfd中删除一个fd;
  • fd:是需要监听的fd
  • *event:是告诉内核需要监听什么事,struct epoll_event结构如下:
typedef union epoll_data {
    void *ptr;
    int fd;
    __uint32_t u32;
    __uint64_t u64;
} epoll_data_t;

struct epoll_event {
    __uint32_t events; /* Epoll events */
    epoll_data_t data; /* User data variable */
};

events可以是以下几个宏的集合:

EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
EPOLLOUT:表示对应的文件描述符可以写;
EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
EPOLLERR:表示对应的文件描述符发生错误;
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。
EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里

等待事件的产生

//该函数返回需要处理的事件数目,如返回0表示已超时。
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

类似于select()调用。

  • events用来从内核得到事件的集合
  • maxevents告之内核这个events有多大,这个 maxevents的值不能大于创建epoll_create()时的size
  • timeout是超时时间(毫秒,0会立即返回,-1将不确定,也有说法说是永久阻塞)。
epoll的内部机制

epoll的实现重点有几个关键词红黑树回调函数rdlist

  • 调用epoll_create会返回一个文件描述符fd,里面创建好了用于监听事件的红黑树和用于存放就绪事件的双向链表rdlist

  • 在调用epoll_ctl时,会将监听事件存放到红黑树(红黑树本身插入和删除时间复杂度为LogN,效率是很高的)中,并且与网络适配器绑定一个回调函数。

  • 当监听的事件发生时,将调用之前绑定的回调函数,这个回调函数的作用是将该事件存放到rdlist中去。

  • 调用epoll_wait函数,去查找rdlist中的就绪事件,存放在传入的参数events中,并返回就绪事件的数量。

epoll的两种模式
  • 水平触发(LT level triggered)
    LT模式是epoll的缺省模式,在该模式下,如果某个事件就绪了,但是并没有被处理,下一次内核还是会继续通知你,poll就是这种模式的一个代表。
  • 边缘触发(ET edge triggered)
    ET模式是一种非常高速的工作方式,它与LT的区别在于,如果第一次通知了用户事件就绪,不论该事件是否被处理,都不会再次通知了,也就是调用epoll_wait无法再获取到该事件。
    在使用边缘触发时,建议使用非阻塞I/O,如果使用阻塞I/O可能会造成永久阻塞。

总结

以上简单介绍了一下select,poll,epoll,在介绍的过程中也简述了他们之间的差别
相对于epoll的触发式,select、poll的轮询方式效率低下,但是每种机制都有自己的适用场景,epoll并不是在所有场景下都是最好的。
试想这样一个场景,selectepoll监听的事件数量一样,这些事件大都处于活跃状态,select只要通过一次循环就可以找到他们,但是epoll则会频繁地调用回调函数,这样做的效率必定是低于一次循环的效率的。
epoll并不适用于活跃事件较多的情况。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值