epoll函数原理和使用介绍

        在该博文中,我们已经了解了什么是 epoll 及其优点,那么实际应用应该如何编写代码呢?epoll 的编码离不开三个基本的函数:epoll_create,epoll_ctl,epoll_wait,下面将介绍 epoll 这三个函数的使用。

        有些 epoll 函数原理需要配合 epoll 函数的源码才能进行深入讲解,这里我们参考 Github 用户 wangbojing 的 NtyTcp 项目中关于 epoll 的实现代码做讲解,可以从这里下载项目源码。

epoll函数原理和使用介绍

1. epoll_create

函数原型 :int epoll_create(int size);

功能说明 :创建一个 epoll 对象,返回该对象的描述符,注意要使用 close 关闭该描述符。

参数说明 :从 Linux 内核 2.6.8 版本起,size 这个参数就被忽略了,只要求 size 大于 0 即可。

原理讲解 

        调用 epoll_create 创建一个 epoll 对象的时候究竟干了什么事情呢?我们可以从 NtyTcp 的代码中查看 epoll_create 的实现代码,最核心的代码就是 struct eventpoll *ep = (struct eventpoll *)calloc(1,sizeof(struct eventpoll)) 创建一个 struct eventpoll 对象,然后对其成员进行初始化。

        struct eventpoll 的定义如下,rbr 是一棵红黑树,支持快速查找键值对,所有需要加入监听事件的描述符都需要添加到这棵红黑树来,rbcnt 记录红黑树节点个数。rdlist 是一个双向链表,当红黑树上监听的描述符发生对应的监听事件时,内核会将这个节点插入到该双向链表来,rdnum 是双向链表节点的个数,也就是发生了事件的节点个数。

2. epoll_ctl

函数原型 :int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

功能说明 :操作控制 epoll 对象,主要涉及 epoll 红黑树上节点的一些操作,比如添加节点,删除节点,修改节点事件。

参数说明

epfd:通过 epoll_create 创建的 epoll 对象句柄。

op:对红黑树的操作,添加节点、删除节点、修改节点监听的事件,分别对应 EPOLL_CTL_ADD,EPOLL_CTL_DEL,EPOLL_CTL_MOD。

添加事件:相当于往红黑树添加一个节点,每个客户端连接服务器后会有一个通讯套接字,每个连接的通讯套接字都不重复,所以这个通讯套接字就是红黑树的 key。

修改事件:把红黑树上监听的 socket 对应的监听事件做修改。

删除事件:相当于取消监听 socket 的事件。

fd:需要添加监听的 socket 描述符,可以是监听套接字,也可以是与客户端通讯的通讯套接字。

event:事件信息。

关于event参数的讲解

        该参数是 struct epoll_event 类型的指针变量,结构体定义如下。

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

struct epoll_event 
{
  uint32_t     events;      /* Epoll 事件 */
  epoll_data_t data;        /* 用户数据 */
};

events成员

        成员 events 代表要监听的 epoll 事件类型,有读事件,写事件,有如下取值。

events参数取值                                                                            含义
EPOLLIN

监听 fd 的读事件。举例:如果客户端发送消息过来,代表服务器收到了可读事件。

EPOLLOUT监听 fd 的写事件。如果 fd 对应的发数据内核缓冲区不为满,只要监听了写事件,就会触发可写事件。
EPOLLRDHUP监听套接字关闭或半关闭事件,Linux 内核 2.6.17 后可用。
EPOLLPRI监听紧急数据可读事件。

举例:假设现在我们的服务器通过调用 accept 函数成功与客户端建立连接并得到了通讯套接字 connfd,如果我们需要关心这个客户端是否给服务器发送数据过来(读事件),我们需要将 event 参数的 events 成员的 EPOLLIN 置位为1,相当于 event.events |= EPOLLIN,如果我们并不关心服务器是否可以往客户端写数据(写事件),我们可以将 event 参数的 events 成员的 EPOLLOUT 置位为 0,相当于 event.events &= ~EPOLLOUT。同理,如果你还关心某个事件或者不关心某个事件,就将该位置为 1 或者 0 即可,再通过 epoll_ctl 函数来修改。这样,当我们调用 epoll_wait 函数等待事件时,一旦发生了我们希望监听的事件时,epoll_wait 函数就会返回并通知我们哪个描述符发生了对应的事件。(本文后续会讲到 epoll_wait 函数)

data成员

        data 成员时一个联合体类型,它可以在我们调用 epoll_ctl 给 fd 添加/修改描述符监听的事件时顺带一些数据。最典型的用法就是每个通讯套接字会对应内存中的一块数据区,这块数据区一般存放着一些连接相关的信息,比如对端的 IP,端口等。当我们要添加该通讯套接字监听事件时就可以把这块内存的地址赋值给 ptr,这样当我们调用 epoll_wait 时也可以取出这些信息。

3. epoll_wait

函数原型 :int epoll_wait(int epid, struct epoll_event *events, int maxevents, int timeout);

功能说明 :阻塞一段时间并等待事件发生,返回事件集合,也就是获取内核的事件通知。说白了就是遍历双向链表,把双向链表里的节点数据拷贝出来,拷贝完毕后就从双向链表移除。

参数说明

epid:epoll_create 返回的 epoll 对象描述符。

events:存放就绪的事件集合,这个是传出参数。

maxevents:代表可以存放的事件个数,也就是 events 数组的大小。

timeout:阻塞等待的时间长短,以毫秒为单位,如果传入 -1 代表阻塞等待。

返回值说明

返回值含义
>0代表有几个我们希望监听的事件发生了
=0timeout 超时时间到了
<0出错,可以通过 errno 值获取出错原因

参考文献

118-epoll(函数)_进击的小学生-CSDN博客

  • 22
    点赞
  • 70
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: `epoll` 函数是 Linux 系统中一个用于处理大量并发连接的 I/O 多路复用机制。它通过维护一个文件描述符集合来监测多个描述符的状态,以便在发生 I/O 事件时快速通知程序。 使用 `epoll` 的基本流程如下: 1. 创建 `epoll` 句柄:使用 `epoll_create` 或 `epoll_create1` 函数创建一个 `epoll` 句柄。 2. 注册文件描述符:使用 `epoll_ctl` 函数向 `epoll` 句柄中添加需要监测的文件描述符,并为每个文件描述符设置监测事件。 3. 进行等待:使用 `epoll_wait` 函数阻塞等待,直到有一个或多个文件描述符准备就绪。 4. 处理事件:当有文件描述符就绪时,处理相应的 I/O 事件。 5. 重复步骤 3 和 4,不断监测文件描述符的状态,处理 I/O 事件。 常见的使用方式是:创建一个线程,在线程中调用 `epoll_wait` 函数,当有文件描述符就绪时通过回调函数处理相应的 I/O 事件。 ### 回答2: epoll函数是Linux系统中一种高效的I/O事件通知机制,用于管理大量的文件描述符。其使用方式如下: 1. 创建一个epoll句柄: int epoll_create(int size); 创建一个epoll实例,并返回一个文件描述符,size表示期望监听的文件描述符数量,通常可以设置为任意正整数。 2. 注册文件描述符和事件: int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); epfd为epoll实例的文件描述符,op为操作类型(EPOLL_CTL_ADD表示添加,EPOLL_CTL_MOD表示修改,EPOLL_CTL_DEL表示删除),fd为需要监听的文件描述符,event为事件类型结构体指针。 3. 开始监听文件描述符事件: int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout); epfd为epoll实例的文件描述符,events为存储事件的数组,maxevents表示最大监听的事件数量,timeout表示等待时间(-1表示一直等待,0表示立即返回,>0表示超时时间)。 4. 对返回的事件进行处理: 在epoll_wait函数返回后,可以遍历events数组,根据每个事件的文件描述符和事件类型进行处理。 5. 关闭epoll实例: close(epfd); 使用epoll实例后,需要调用close函数关闭,释放相关资源。 以上就是epoll函数的基本使用流程。通过epoll可以高效地监听大量的文件描述符事件,减少系统资源的消耗。在实际开发中,可以根据需要设置不同的事件类型和回调函数,实现具体的业务逻辑。 ### 回答3: epoll函数是Linux中用于处理I/O事件的一种高效机制。它可以监视一组文件描述符,并在其中的任意一个文件描述符上发生事件时进行相应的处理。 使用epoll函数的基本步骤如下: 1. 调用epoll_create函数创建一个epoll的句柄,该句柄被用于后续的相关操作。 2. 使用epoll_ctl函数epoll句柄中注册需要监视的文件描述符和事件。通过该函数可以实现添加、修改和删除文件描述符以及相应事件的功能。 3. 使用epoll_wait函数等待事件的发生。epoll_wait会一直阻塞,直到有文件描述符上的事件发生。一旦有事件发生,epoll_wait会返回所发生事件的文件描述符和相应的事件类型。 4. 根据返回的事件类型,进行相应的处理。 epoll函数有三个基本的系统调用: 1. epoll_create函数用来创建一个epoll实例,返回一个epoll句柄。 2. epoll_ctl函数用于操作epoll实例,可以实现添加、修改和删除文件描述符以及相应事件的功能。 3. epoll_wait函数用于等待事件的发生,一旦事件发生则返回相应的文件描述符和事件类型。 epoll函数使用优点包括: - 支持大量的连接,可以监视数万个文件描述符。 - 存储监视文件描述符的数据结构(epoll实例)可以重复利用,避免了每次都需要重新设置的问题。 - 使用epoll_wait函数进行等待事件的发生,避免了轮询的方式,提高了效率。 总之,epoll函数是Linux中用于处理I/O事件的一种高效机制,可以通过创建、操作epoll实例和等待事件的发生来实现对文件描述符的监视和相应事件的处理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

椛茶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值