IO多路转接之select、poll、epoll简介及优缺点对比

IO多路转接

IO多路转接也叫做IO多路复用,它是一种处理高并发的IO事件监控。它可以同时对大量的描述符进行监控,监控其是否具备了IO条件。
就绪:包括了读就绪事件(就是有数据到来的时候),写就绪事件(缓冲区有空闲的空间),异常事件(发生异常)。对于服务器来说,很多时候我们都是监控的读事件,对于写事件和异常事件都只会在特定的情况下使用。

IO多路转接之select

select简介

系统提供了select系统调用来实现多路复用的输入输出模型。

  • select系统调用是用来让我们的程序监视多个文件描述符的状态变化的
  • 程序会停在select这里等待,直到被监视的文件描述符有一个或多个发生了状态改变

select函数原型

头文件: #include <sys/select.h>

 int select(int nfds, fd_set *readfds, fd_set *writefds,
			 fd_set *exceptfds, struct timeval *timeout)
  • nfds是需要监视的最大的文件描述符值+1
  • readfds,writefds,exceptfds分别对应于需要检测的可读文件描述符的集合,可写文件描述符的集 合及异常文件描 述符的集合;
  • timeout为结构timeval,用来设置select()的等待时间

timeout:

  • NULL:则表示select()没有timeout,select将一直被阻塞,直到某个文件描述符上发生了事件;
  • 0:仅检测描述符集合的状态,然后立即返回,并不等待外部事件的发生。
  • 固定的时间值:如果在指定的时间段里没有事件发生,select将超时返回。

返回值

  • 执行成功则返回文件描述词状态已改变的个数
  • 如果返回0代表在描述词状态改变前已超过timeout时间
  • 当有错误发生时则返回-1,错误原因存于errno,此时参数readfds,writefds, exceptfds和timeout的值变成不可预测。

fd_set结构体

fd_set 是一个结构体,严格来说它是一个位图,通过使用位图中对应的位来表示要监视的文件描述符。它提供了一组操作fd_set的接口, 来比较方便的操作位图.

 void FD_CLR(int fd, fd_set *set); // 用来清除描述词组set中相关fd 的位
 int FD_ISSET(int fd, fd_set *set); // 用来测试描述词组set中相关fd 的位是否为真
 void FD_SET(int fd, fd_set *set); // 用来设置描述词组set中相关fd的位
 void FD_ZERO(fd_set *set); // 用来清除描述词组set的全部位

select缺点

  • 每次调用select, 都需要手动设置fd集合, 使用起来非常不便.
  • 每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大
  • 同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大
  • select支持的文件描述符数量太小

IO多路转接之poll

poll函数简介

#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
// pollfd结构
struct pollfd {
 int fd; /* file descriptor */
 short events; /* requested events */
 short revents; /* returned events */
};

参数说明

  • fds是一个poll函数监听的结构列表. 每一个元素中, 包含了三部分内容: 文件描述符, 监听的事件集合, 返 回的事件集合.
  • nfds表示fds数组的长度.
  • timeout表示poll函数的超时时间, 单位是毫秒

events和revents的取值

返回值说明

  • 返回值小于0, 表示出错;
  • 返回值等于0, 表示poll函数等待超时;
  • 返回值大于0, 表示poll由于监听的文件描述符就绪而返回

poll优缺点

优点

  • pollfd结构包含了要监视的event和发生的event,不再使用select“参数-值”传递的方式. 接口使用比select更加简单。
  • poll并没有最大数量限制 (但是数量过大后性能也是会下降).

缺点

  • 和select函数一样,poll返回后,需要轮询pollfd来获取就绪的描述符.
  • 每次调用poll都需要把大量的pollfd结构从用户态拷贝到内核中.
  • 同时连接的大量客户端在一时刻可能只有很少的处于就绪状态, 因此随着监视的描述符数量的增长, 其效 率也会线性下降.

IO多路转接之epoll

epoll 是Linux下性能最高的IO多路转接模型。epoll 与select和poll在使用和实现上有很大区别。首先,epoll使用一组函数来完成,而不是单独的一个函数;其次,epoll把用户关心的文件描述符上的事件放在内核里的一个事件表中,无须向select和poll那样每次调用都要重复传入文件描述符集合事件集。

epoll相关的系统调用

  1. epoll_create
// #include <sys/epoll.h>
//创建一个epoll句柄
	int epoll_create(int size); 
//自从linux2.6.8之后,size参数是被忽略的.
//用完之后, 必须调用close()关闭.
  1. epoll_ctl
//epoll的事件注册函数,在内核的eventpoll结构中添加移除,修改所监控的事件结构。
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

//第二个参数
EPOLL_CTL_ADD :注册新的fd到epfd中;
EPOLL_CTL_MOD :修改已经注册的fd的监听事件;
EPOLL_CTL_DEL :从epfd中删除一个fd

它不同于select()是在监听事件时告诉内核要监听什么类型的事件, 而是在这里先注册要监听的事件类型.

  • 第一个参数是epoll_create()的返回值(epoll的句柄).
  • 第二个参数表示动作,用三个宏来表示.
  • 第三个参数是需要监听的fd.
  • 第四个参数是告诉内核需要监听什么事

struct epoll_event 结构如下

struct epoll_event
{
    __int32_t events;       //epoll事件
    epoll_data_t data;      //用户数据
};

typedef union epoll_data
{
    void *ptr;
    int  fd;
    uint32_t u32;
    uint64_t u64;
}epoll_data;

在使用epoll_ctl时,是把fd添加、修改到内核事件表中,或从内核事件表中删除fd的事件。如果是添加事件到事件表中,可以往data中的fd上添加事件events,或者不用data中的fd,而把fd放到用户数据ptr所指的内存中(因为epoll_data是一个联合体,只能使用其中一个数据),再设置events。

  1. epoll_wait
//它在一段时间内等待一个组文件描述符上的事件。
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
  • timeout参数和select与poll相同,指定一个超时时间
  • maxevents指定最多监听多少个事件
  • events是一个传出型参数,epoll_wait函数如果检测到事件就绪,就将所有就绪的事件从内核事件表(epfd所指的文件)中复制到events指定的数组中。这个数组用来输出epoll_wait检测到的就绪事件,而不像select与poll那样

epoll的底层实现

当某一进程调用epoll_create方法时,Linux内核会创建一个eventpoll结构体,这个结构体中有两个成员与epoll的使用方式密切相关,一个是红黑树,另一个是双向链表。红黑树中存储着所有添加到epoll中的需要监控的事件,双链表中则存放着将要通过epoll_wait返回给用户的满足条件的事件。这些事件都会挂载在红黑树中,如此,重复添加的事件就可以通过红黑树而高效的识别出来(红黑树的插
入时间效率是log(n),其中n为树的高度).而所有添加到epoll中的事件都会与设备(网卡)驱动程序建立回调关系,也就是说,当响应的事件发生时会调用这个回调方法.这个回调方法在内核中叫ep_poll_callback,它会将发生的事件添加到rdlist双链表中.通过这种方式大大的提高了效率。

epoll的工作模式

epoll 对文件描述符的操作有两种模式:LT(level trigger:水平触发模式,default)和 ET(edge trigger:边沿触发模式)。

  • LT模式:当 epoll_wait 检测到描述符事件发生并将此事件通知应用程序后,应用程序可以不处理该事件,下次调用 epoll_wait 时,会再次向应用程序通知此事件,直到事件被处理,可能会重复处理事件。
  • ET 模式:当 epoll_wait 检测到描述符事件发生并将此事件通知应用程序后,应用程序必须立即处理该事件,如果不处理,下次调用 epoll_wait 时,不会再次向应用程序通知此事件,可能会丢失事件处理。

epoll的优缺点

  • 优点
    1)epoll没有监控的上线
    2)采用事件结构简化了select监控集合的监控流程
    3)epoll是一个异步阻塞操作,发起调用,让操作系统进行文件描述符的监控,使用事件回调函数对描述符进行监控,避免了select的遍历轮询,性能不会随着文件描述符增多而下降
    4)epoll发起调用进行等待,循环判断内核中epoll就绪时间链表是不是为空来确定是否有就绪事件,若有就绪事件,则将对应的事件拷贝到用户态,直接告诉了用户那些描述符就绪了,不需要循环判断。
    5)epoll描述符的事件结构,只需要向内核中拷贝一次,不需要每次都拷贝
  • 缺点
    1)不能跨平台
    2)延时时间只能精确到毫秒
  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值