117-epoll(基础)

epoll 是在 linux 2.6 内核之后才引入的新技术,它也是一种 IO 多路复用技术。之前已经学过了 select 和 poll,为什么还要学习新的东西?新事物的出现必然有它的道理,来分析一下前面学习过的 select 和 poll 来说明。

1. IO 事件

实际上,前面的 select 和 poll 已经提过很多次事件的概念了,比如有可读事件,可写事件,实际上这并不很准确。

在 IO 中,内核中都有一个缓冲区。写 IO 时,数据是写到缓冲区中,而读 IO 时,是从缓冲区中读数据,所以缓冲区就是一块临时内存空间。系统这样设计,是为了提高执行效率的,因为频繁的操作硬件(比如硬盘、网卡)是相当耗时的,所以为了能减少这种操作,就使用缓冲区,等缓冲区的数据达到一定量的时候,一次性写进硬盘或网卡。

事件,是对于缓冲区来说的。

如果缓冲区中的数据发生了变化,说明有 IO 事件产生。比如从空缓冲区变非空缓冲区、缓冲区中的的数据增多、缓冲区中的数据减少等等,这些都可以产生 IO 事件。

一旦有 IO 事件产生,就意味着对应的描述符可读、可写或者发生了异常。

2. select 与 poll的缺点

  • 需要自己判断哪个描述符发生事件

虽然 select 和 poll 可以帮助我们同时“监听”IO 事件(待会再详细讲解什么是事件,前面的文件也出现了很多次了),但是它有一个毛病是在 select 或 poll 返回后,需要我们自己一个一个去查询是哪个描述符上发生了 IO 事件。

回忆前面的的程序,对于 select 来说,我们要把所有的描述符都判断一遍是否在返回的描述符集合中(因为只有集合中出现的描述符才有 IO 事件发生)。本质上这局限于 fd_set 集合没有为我们提供一种遍历元素的方法。对于 poll 函数来说也是一样的,它需要我们自己去检查所有 poll_fd 数组中的描述符是否发生了事件。对于这样的操作,需要花费的复杂度是 O(n)。

epoll 则不一样,它能够将所有发生事件的描述符保存到数组中,而没有发生事件的描述符不会保存进数组的。这意味着你已经不再需要去遍历所有描述符来判断谁发生了 IO 事件。

  • 描述符复制

另一个方面,当我们使用 select 或 poll 的时候,每一次都需要将想要监听的描述符传递给它。这意味着每一次调用 select 和 poll 都需要将这些描述符复制到内核空间。

而 epoll 的优点是它只要事先复制一次,以后再也不用管了。

3. epoll

说了这么多 epoll 的好处,可以大家还是没见过它,也不知道为什么它会这样好。epoll 能够做到这一点,是因为它不是一个函数,而是 3 个函数。

使用 epoll 函数的步骤通常如下:

  • 首先创建一个 epoll 对象,并返回该对象的描述符
  • 通过该对象的描述符,将你想要监听的描述符复制到内核
  • 开始监听事件

这三个步骤对应三个函数,它们的原型如下:

int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

看到如此众多复杂的参数,同学们可能懵圈了,不过先不要慌乱,先用最简单的伪代码来说明怎么用,然后具体的使用方法请继续阅读后续文章。

// 先创建一个 epoll 对象,然后拿到它的描述符,参数 3 表示我想监听 3 个描述符。
epfd = epoll_create(3);

// 把你想要监听的描述符添加到 epoll 对象中。
epoll_ctl(epfd, fd0, EPOLLIN); // 监听可读事件
epoll_ctl(epfd, fd1, EPOLLIN); // 监听可读事件
epoll_ctl(epfd, fd1, EPOLLOUT); // 监听可写事件

while(1) {
  // 开始监听 IO 事件,将发生事件的描述会放到事件集合 events_set 中
  events_set = epoll_wait(epfd);
  // 依次处理 events_set 中的所有描述符
  for (evt : events_set) {
    // 判断事件类型
    if (evt.events & EPOLLIN) {
      read(evt.fd);
    }
    if (evt.events & EPOLLOUT) {
      write(evt.fd);
    }
    if (evt.events & OTHER) {
      // 比如异常
    }
  }
}

这样看起来,我们的 epoll 似乎比 select 和 poll 要好用太多太多,是不是?

4. 总结

  • 理解什么是 IO 事件
  • 知道 select、poll 和 epoll 的区别
  • 知道 epoll 的基本使用流程
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值