120-epoll(边沿模式)

上一篇文章非常详细的讲解了两种触发模式。那么,到底是使用水平触发模式好呢,还是使用边沿触发模式好呢?

1. 边沿触发 + 非阻塞

再讨论这个问题前,我们还有一个问题没解决,就是上一篇文章中使用边沿模式还遗留了一个问题,即因为 read 的接收缓冲区太小(只有 4 字节),这导致每次缓冲区的数据读取不完,从而在下次数据到来前,即使缓冲区还有剩余数据未读取,epoll_wait 函数也会阻塞。

解决此问题的方法就是使用 while 循环反复从缓冲区读数据,直到全部读完为止,但是这容易发生危险!因为我们不知道什么时候缓冲区数据什么时候读完,一旦真的读完了,read 就会发生阻塞。所以,这里应该改用 Non-blocking,即非阻塞的方式去 read,如果 read 返回值 < 0 同时 errno = EAGAIN 或 errno = EWOULDBLOCK 了,就说明缓冲区数据读完了,此时应该正常退出读循环。

所以,上一篇文章的代码只需要修改两个地方。为了以示区别,修改后的程序命名为 epoll_et_nio.c.

  • 修改以非阻塞的方式打开管道
  fds[1] = open("a.fifo", O_RDONLY | O_NONBLOCK);
  printf("open pipe: fd = %d\n", fds[1]);
  fds[2] = open("b.fifo", O_RDONLY | O_NONBLOCK);
  • 修改 process 函数,以循环方式 read
int process(char* prompt, int fd) {
  int n;
  char buf[4];
  char line[64];
  sprintf(line, "%s say: ", prompt);

  // 开始循环 read
  while(1) {
    n = read(fd, buf, 3);
    if (n < 0) {
      // 如果 errno 的值是 EAGAIN 或 EWOULDBLOCK,说明缓冲区数据读完了。
      if (errno == EAGAIN || errno == EWOULDBLOCK)
        break;
      PERR("read");
    }
    else if (n == 0) {
      // 对端关闭
      sprintf(line, "%s closed\n", prompt);
      puts(line);
      return 0;
    }
    else if (n > 0) {
      buf[n] = 0;
      // 把从 read 读到的内容串接到 line 后面。
      strcat(line, buf);
    }
  }
  puts(line);
  return n;
}

接下来就可以编译运行了。


这里写图片描述
图1 运行结果

2. 两种触发模式的优缺点

假设每次 read 都不能一次性缓冲区数据读完。

首先从程序运行的角度上看,水平模式+阻塞读,只要缓冲区还有数据,每次都会触发 epoll_wait 函数返回。

边沿模式+非阻塞读,只触发一次 epoll_wait 返回,然后 read 循环读。

那么效率就体现在

  • while { epoll_wait + read }
  • epoll_wait + while {read}

个人不对这两种谁快做讨论,因为目前还没有谁证明过后者比前者快。

另一方面,对于水平模式来讲,如果监听了 EPOLLOUT 事件,可能会导致每次都会触发该事件,而边沿模式则不会。所以水平模式下总触发 EPOLLOUT 事件感觉就是在浪费 CPU.

有很多大名鼎鼎的网络库或框架,都使用了水平触发模式,而 Nginx 使用了边沿触发模式。

所有这两种方式各有利弊,如果你想了解更多,请自行谷歌,这里还有知乎上的一篇文章是关于对这两种触发模式的讨论——传送门.

3. 总结

  • 掌握边沿触发+非阻塞 IO
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值