美文网首页
I/O复用模型:epoll

I/O复用模型:epoll

作者: 404Not_Found | 来源:发表于2021-08-16 07:59 被阅读0次
  • 作者: 雪山肥鱼
  • 时间:20210816 22:08
  • 目的:了解 epoll 模型

epoll 的不同之处

用法不同之处

epoll 是 linux下多路复用IO接口 select/poll 的增强版, 它能显著提高程序在大量并发链接中只有少量活跃情况下的系统CPU利用率。
原因:

  1. 不用在每次等待事件之前,都必须重新准备要被侦听的文件描述符集合。(无需事先准备监控容器)
  2. 获取事件的时候,无需遍历监控容器,从而找到监控集(fd != -1),并且找到真正有I/O的文件描述符。

所以只要比哪里哪些被内核IO事件异步唤醒,而加入Ready队列的文件描述符即可。

select legacy 行为:


select.png

select 组织 文件时放在 while(1)中的

/*select*/
while(1) {
  rset = allset //while(1) 中重新组监控文件集合
  select(maxfd + 1, &reset, NULL, NULL, NULL);
}

其实从 poll 开始, 组织文件集就已经放在了 while(1) 外


poll.png
/*
poll
struct pollfd client[OPEN_MAX];
*/
while(1) {
  poll(client, maxfd+1, -1);
}

epoll 的实现 更是如此。poll/epoll 都是好莱坞模式,监控的事件提前注册好,等着演就行了。

反应堆的不同

select/poll 反应堆.png epoll 反应堆.png

epoll 反应堆是一棵二叉树, 想监控哪个fd就放进来。
epoll会监控这棵树,哪个fd 的 读/写 触发了。最终会在用户空间返回一个ready的就绪队列。即反应集。

平衡二叉树查找的事件复杂度是 log(2n);

所以epoll 拿到的是一个真正的反应集合。
有一种说法是,epoll 适合大并发量,少活跃度的情况(就是连接虽然多,但是请求很少)。有待验证。

  • select/poll
    返回整个监控容器.png
    返回IO个数,以及带着命中的整个靶子,命中的I/O的整个监控容器
    需要从监控集里过滤出反应集,再过滤出命中点。
    返回的是个靶子
  • epoll
    返回的是靶子上的命中点!

epoll API

  1. 创建epoll句柄,参数size 告诉内核监听额文件描述符个数,根内存大小有关
int epoll_create(int size);
  1. 控制某个epoll监控的FD 中的事件:注册、修改、删除
int epoll_ctl(int epfd, int op, int fd, struct epoll_event * event);

epfd: 为epoll_create 的句柄
op: 为表示的动作,用3个宏来表示

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

event: 告诉内核需要监听的事件

struct epoll_event {
  _unit32t events; /*Epoll Events*/
  epoll_data_t data; /*User data varaible*/
};

/*event*/
EPOLLIN:对应的fd可以读(包括对端socket正常关闭)
EPOLLOUT: 对应的fd描述符可写
EPOLLPRI: 对应的fd有紧急的数据可读(非外带数据)
EPOLLERR: 对应的fd 发生错误
EPOLLHUP: 对应的fd被挂起
EPOLLET:监控的fd 设为边缘触发模式(edge triggered)
EPOLLONESHOT: 只监控一次,监听完这次事件后,如果还想监听,则还需要手动加到EPOLL队列中。

/*联合体,看具体情况使用哪个*/
typedef union epoll_data {
  void *ptr;
  int     fd;
  uint32_t   u32;
  uint64_t   u64;
} epoll_data_t;
  1. 等待监控文件描述符上有事件产生
#include <sys/epoll.h>
int epoll_wait(int epfd, struct epoll_event* events, int maxevents, int timeout);

events: 用来从内核中拿到命中靶子的命中点
maxevents: 告知内核这个evets多大,这个maxevents的值不能大于创建epoll_create()的size.
timeout: 超时 事件
return,nready 有IO反应的个数

epoll 代码举例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <errno.h>
#include <ctype.h>
#include "my_socket.h"
#define MAXLINE 80
#define SERV_PORT 8888
#define OPEN_MAX 1024

int main(int argc, char **argv)
{
   int i, j, listenfd, connfd, sockfd;
   int nready, efd, res;
   ssize_t n;
   char buf[MAXLINE], str[INET_ADDRSTRLEN];

   struct sockaddr_in cliaddr, servaddr;
   struct epoll_event tep, ep[OPEN_MAX];

   listenfd = Socket(AF_INET, SOCK_STREAM, 0);

   bzero(&servaddr, sizeof(servaddr));
   servaddr.sin_family = AF_INET;
   servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
   servaddr.sin_port = htons(SERV_PORT);

   Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
   Listen(listenfd, 20);

   efd = epoll_create(OPEN_MAX);
   if(efd == -1)
       perr_exit("epoll_create");

   tep.events = EPOLLIN;
   tep.data.fd = listenfd;
   res = epoll_ctl(efd, EPOLL_CTL_ADD, listenfd, &tep);
   if(res == -1)
       perr_exit("epoll_ctl");

   while(1)
   {
       nready = epoll_wait(efd, ep, OPEN_MAX, -1);

       if(nready == -1)
           perr_exit("epoll_wait");
       for(i = 0; i < nready; i++)
       {
           if((ep[i].events & EPOLLIN))
               continue;
           if(ep[i].data.fd == listenfd)
           {
              connfd = Accept(listenfd, (struct sockaddr*)&cliaddr, &clilen);
              printf("received from %s at PORT %d\n",
                     inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
                     noths(cliaddr.sin_port));

              tep.events = EPOLLIN;
              tep.data.fd = connfd;
              res = epoll_ctl(efd, EPOLL_CTL_ADD, connfd, &tep);
              if(res == -1)
                  perr_exit("poll_ctl");
           }
           else
           {
               sockfd = ep[i].data.fd;
               n = Read(sockfd, buf, MAXLINE);
               if(n == 0)
               {
                   res = epoll_ctl(efd, EPOLL_CTL_DEL, sockfd, NULL);
                   if(res == -1)
                       perr_exit("epoll_ctl");
                   Close(sockfd);
                   printf("client[%d] closed connection\n", sockfd);
               }
               else
               {
                  for(j = 0; j< n; j++)
                      buf[j] = toupper(buf[j]);
                  Write(sockfd, buf, n);
               }
           }
       }
   }

    close(listenfd);
    close(efd);

    return 0;
}

代码分析:

  • 创建反应堆,即创建树
int efd = epoll_create(OPEN_MAX);
  • 创建事件,以及“靶心”容器(队列,数组,双向链表等)
struct epoll_event tep;
tep.event = EPOLLIN;
tep.data.fd = listenfd;

struct epoll_event ep[OPEN_MAX];
  • 注册事件
res = epoll_ctl(efd, EPOLL_CTL_ADD, listenfd, &tep);
  • 开始检测
nready = epoll_wait(efd, ep, OPEN_MAX, -1);
  • 解复用
    直接循环 nready 即可, 相当于遍历 "靶心容器"
for( i = 0; i < nready; i++ )
{
    if(!(ep[i].events & EPOLLIN)
        continue;
    if(ep[i].data.fd == listenfd)
    {
        /* 对于listenfd 的处理 */
    }
    else
    {
      /*对于 connfd的处理*/
    }
}

相关文章

  • netty

    I/O 模型 阻塞I/O模型(BIO) 非阻塞I/O模型 I/O复用模型(select/poll;epoll)se...

  • redis多路 I/O 复用

    多路 I/O 复用模型 多路I/O复用模型是利用 select、poll、epoll 可以同时监察多个流的 I/O...

  • IO多路复用

    I/O 多路复用 多路I/O复用模型是利用 select、poll、epoll 可以同时监察多个流的 I/O 事件...

  • Poll

    Linux下有三种I/O复用模型,select、poll、epoll 为什么要用I/O复用模型呢? 计算机资源有限...

  • 2019-07-08

    epoll基础知识 多路复用 1、阻塞 I/O 只能阻塞一个 I/O 操作,而 I/O 复用模型能够阻塞多个 I/...

  • Linux下I/O多路复用select, poll, epoll

    Linux下I/O多路复用select, poll, epoll 三种模型 select, poll, epoll...

  • Uinx系统下的I/O模型

    5种I/O模型的基本区别: 阻塞式I/O 非阻塞式I/O I/O复用(select,poll,epoll) 信号驱...

  • epoll的简单使用

    epoll的功能:实现I/O复用,即多路I/O。 一、epoll的系统调用函数 epoll只有epoll_crea...

  • I/O模型/同步异步阻塞非阻塞

    I/O模型 阻塞式IO 非阻塞式IO I/O多路复用(select,poll,epoll) 信号驱动式IO 异步I...

  • 其他 I/O 模型总结

    I/O 多路复用 信号驱动I/O Linux专有epoll I/O多路复用 通过select系统调用或者poll系...

网友评论

      本文标题:I/O复用模型:epoll

      本文链接:https://www.haomeiwen.com/subject/ndivbltx.html