Epoll入门理解及使用示例

在linux网络编程中,相信epoll的使用一定必不可少。大家都知道传统的select在使用过程中不仅对鉴定的事件数目上有一定限制,事件到达一定数量后,效率也会降低,那么在实际应用中呃epoll的使用就脱颖而出了。那么本文就是针对于初识epoll的伙伴的一个入门小demo,文章中的代码示例转载自国外网站,相对于cdsn很多博客上的代码示例我认为更方便理解。

文章分为三个部分,分别依次加入对epoll中三个函数:
epoll_create(int size);
epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout)
的使用

在以下的代码中将使用这些头文件

#include <stdio.h> // 用于 fprintf()
#include <unistd.h> // 用于close(), read()
#include <sys/epoll.h> // 用于 epoll_create1(), epoll_ctl(), struct epoll_event
#include <string.h> // 用于 strncmp

Step1 创建epoll文件描述符

首先将完成对epoll实例的创建和关闭过程:

#include <stdio.h> // 用于 fprintf()
#include <unistd.h> // 用于 close()
#include <sys/epoll.h> // 用于 epoll_create1()
int main( ) 
{
  int epoll_fd = epoll_create1 ( 0 ) ;
 
  if(epoll_fd == - 1{
    fprintf ( stderr, "创建 epoll 文件描述符失败\n" ) ;
    return 1 ; 
  }
 
  if(close(epoll_fd ))
  {
    fprintf ( stderr, "无法关闭 epoll 文件描述符\n" ) ;
    return 1 ; 
  }
  return 0 ; 
}

epoll_create(int size):size用来告诉内核这个监听的数目一共有多大。需要注意的是,当创建好epoll句柄后,它就是会占用一个fd值,在linux下如果查看/proc/进程id/fd/,是能够看到这个fd的,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。

Step2 为epoll添加文件描述符来观察

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
epoll的事件注册函数,它不同与select()是在监听事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型。第一个参数是epoll_create()的返回值, 第二个参数表示动作,用三个宏来表示:
EPOLL_CTL_ADD:注册新的fd到epfd中;
EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
EPOLL_CTL_DEL:从epfd中删除一个fd;
第三个参数是需要监听的fd,第四个参数是告诉内核需要监听什么事

#include <stdio.h>     // for fprintf()
#include <unistd.h>    // for close()
#include <sys/epoll.h> // for epoll_create1(), epoll_ctl(), struct epoll_event
 
int main()
{
  struct epoll_event event;
  int epoll_fd = epoll_create1(0);
 
  if(epoll_fd == -1)
  {
    fprintf(stderr, "Failed to create epoll file descriptor\n");
    return 1;
  }
 
  event.events = EPOLLIN;
  event.data.fd = 0;
 
  if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, 0, &event))
  {
    fprintf(stderr, "Failed to add file descriptor to epoll\n");
    close(epoll_fd);
    return 1;
  }
 
  if(close(epoll_fd))
  {
    fprintf(stderr, "Failed to close epoll file descriptor\n");
    return 1;
  }
  return 0;
}

在这里,添加了一个 epoll_event 结构的实例,并使用epoll_ctl()将文件描述符 0 添加到我们的 epoll 实例epoll_fd中。我们为最后一个参数传递的事件结构让 epoll 知道我们正在寻找仅观察输入事件EPOLLIN,并让我们提供一些将为事件返回的用户定义数据。

Step3 等待事件的产生

int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
等待事件的产生,类似于select()调用。参数events用来从内核得到事件的集合,maxevents告之内核这个events有多大,这个 maxevents的值不能大于创建epoll_create()时的size,参数timeout是超时时间(毫秒,0会立即返回,-1将不确定,也有说法说是永久阻塞)。该函数返回需要处理的事件数目,如返回0表示已超时。

那么最后我们加入epoll_wait函数,简单展示一下epoll的运行过程

#define MAX_EVENTS 5
#define READ_SIZE 10
#include <stdio.h>     // for fprintf()
#include <unistd.h>    // for close(), read()
#include <sys/epoll.h> // for epoll_create1(), epoll_ctl(), struct epoll_event
#include <string.h>    // for strncmp
 
int main()
{
  int running = 1, event_count, i;
  size_t bytes_read;
  char read_buffer[READ_SIZE + 1];
  struct epoll_event event, events[MAX_EVENTS];
  int epoll_fd = epoll_create1(0);
 
  if(epoll_fd == -1)
  {
    fprintf(stderr, "Failed to create epoll file descriptor\n");
    return 1;
  }
 
  event.events = EPOLLIN;
  event.data.fd = 0;
 
  if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, 0, &event))
  {
    fprintf(stderr, "Failed to add file descriptor to epoll\n");
    close(epoll_fd);
    return 1;
  }
 
  while(running)
  {
    printf("\nPolling for input...\n");
    event_count = epoll_wait(epoll_fd, events, MAX_EVENTS, 30000);
    printf("%d ready events\n", event_count);
    for(i = 0; i < event_count; i++)
    {
      printf("Reading file descriptor '%d' -- ", events[i].data.fd);
      bytes_read = read(events[i].data.fd, read_buffer, READ_SIZE);
      printf("%zd bytes read.\n", bytes_read);
      read_buffer[bytes_read] = '\0';
      printf("Read '%s'\n", read_buffer);
 
      if(!strncmp(read_buffer, "stop\n", 5))
        running = 0;
    }
  }
 
  if(close(epoll_fd))
  {
    fprintf(stderr, "Failed to close epoll file descriptor\n");
    return 1;
  }
  return 0;
}

在这里添加了一些新变量来支持和展示我正在做的事情。我还添加了一个 while 循环,它将继续从被监视的文件描述符中读取,直到其中一个说“停止”。我使用epoll_wait()等待来自 epoll 实例的事件发生,结果将存储在事件数组中,最长为MAX_EVENTS,超时时间为 30 秒。epoll_wait() 的返回值表示事件数组中有多少成员被事件数据填充。除此之外,它只是打印出它得到的东西并做一些基本的逻辑来结束事情。

下面展示一下运行的结果:

$ ./epoll_example 

Polling for input..
#开始输入字符
Hello!

1 ready events
Reading file descriptor '0' -- 7 bytes read.
Read 'hello!
'

Polling for input...

#输入更长的字符
this is too long for the buffer we made

1 ready events
Reading file descriptor '0' -- 10 bytes read.
Read 'this is to'

Polling for input...
1 ready events
Reading file descriptor '0' -- 10 bytes read.
Read 'o long for'

Polling for input...
1 ready events
Reading file descriptor '0' -- 10 bytes read.
Read ' the buffe'

Polling for input...
1 ready events
Reading file descriptor '0' -- 10 bytes read.
Read 'r we made
'

Polling for input...

#输入stop以结束监听
stop

1 ready events
Reading file descriptor '0' -- 5 bytes read.
Read 'stop
'

本文转载及参考自:
https://suchprogramming.com/epoll-in-3-easy-steps/

https://blog.csdn.net/haoren001/article/details/15019829?

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值