Liunx——Epoll机制

Epoll是Linux内核改进的Poll用用于处理大批量文件描述符,是Linux下多路复用I/O接口Select和Poll的增强版,它显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。在当前的很多流行的高并发服务器中都有很高的使用率,例如Nginx、Apache等服务器,都支持Linux的Epoll机制。

Epoll函数接口

Epoll不是一种通用的事件驱动机制,只是运用事件驱动I/O。它与另外两种常用的select、poll模型有着很大的差异。首先,Epoll是运用一组函数来完成任务,而不是单个函数。其次,Epoll通过在内核创建一个事件表,把用户连接的文件描述符上的事件存放上面,避免调用重复的传入文件描述符或事件集。Epoll的工作流程是通过epoll_create()创建一个文件描述符,来标识内核中的事件表。然后通过epoll_ctl()来添加或减少Epoll所监视的文件描述符及其对应的事件,并用epoll_wait()在一段时间内等待返回文件描述上所就绪的事件,超出时间就返回。下面详细介绍关于Epoll的函数原与使用方法。
(1)epoll_create() 原型:
int epoll_create(int size);
用于创建一个int类型的文件描述符,成功会返回创建好的文件描述符,失败返回-1并设置erron值,size代表内核可监听的最大数量及是同时在线的最大并发量。因为创建的是一个文件描述符应用程序通过该接口注册和监听事件,所以在Epoll调用结束的时候必须关闭该接口,否则可能导致fd被耗尽。

(2)epoll_ctl() 原型:
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
用于向内核注册和修改事件操作成功返回0,失败返回-1,。epfd代表Epoll的文件描述符,fd表示要操作的文件描述符,op是代表的操作类型如以下三种:
EPOLL_CTL_ADD:向epfd中添加新的fd;
EPOLL_CTL_MOD:修改已经存在epfd中的fd事件类型;
EPOLL_CTL_DEL: 将fd从epfd中删除;
最后一个参数是与操作的fd相对应的事件struct epoll_event用于代表事件数据其结构如下:
struct epoll_event{
_unint32 events;//epoll事件
epoll_data_t data;//用户数据
}
其中events成员描述事件类型。支持的事件类型宏定义如下:
EPOLLIN :相应的fd可读或者其对端的socket已经关闭了;
EPOLLOUT:相应的fd可以写;
EPOLLPRI: 相应的fd有紧急的数据可读,一般表示外来的数据;
EPOLLERR:相应的fd发生错误;
EPOLLHUP:相应的fd被挂断;
EPOLLET: 这是用于设置fd对应EPOLL的工作模式为边缘触发模式,系统默认的是水平触发模式。
EPOLLONESHOT:表示对应的fd有事件发生时只会向应用程序通知一次,当监听完这次事件之后,若要继续监听这个socket的话,则需要将socket再次加入到EPOLL队列里 data是用来存储用户数据的结构体其定义如下:
typedef union epoll_data {
void *ptr;
int fd;
__uint32_t u32;
__uint64_t u64;
} epoll_data_t;
epoll_data是一个联合结构体,fd是使用最多的成员在运用epoll_wait之后fd代表着有事件发生的文件描述符。ptr成员是用保存用户数据,通常与文件描述关联使用。

(3)epoll_wait() 原型:
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
Epoll系列系统调用的主要函数就是poll_wait,它用于在一段时间内等待epfd里面注册的文件描述符返还事件,函数成功的时候会返回就绪的文件描述符的个数。并将有事件发生的文件描述符对应的event添加到struct epoll_event类型数组里面。失败的时候返回-1,并设置系统的erron值。

Epoll运行机制

Select和Poll模式都是采用轮询的方式,每次调用都要扫描整个注册的文件描述符集合,再将就绪的文件描述符返回给应用程序,其时间复杂度为O(n)。而Epoll采用回调方式,内核检测到就绪的文件描述符时,触发相应的回调函数将对应的事件插入到事件队列。内核在适当的时机将就绪的事件队列拷贝到用户空间,时间复杂度为O(1),从而极大的提升了服务器对数据处理效率。其详细的Epoll机制流程图如图1所示。

在这里插入图片描述

(1) 数据数据储存方式
Epoll运用mmap方法将用户的一块地址和内核空间的一块地址映射到一块相同的物理内存地址,使读写数据的时候直接对物理内存进行操作,绕过了内存缓存。并使用红黑树去存储监听的套接字,这样极大的提升了数据处理效率。

(2) 添加和返回事件
当有新的客户端连接时,Epoll通过epoll_ctl()将新创建的文件描述符添加到红黑树的某个节点,并与相应的设备驱动程序建立回调关系。当储存的文件描述符有事件发生时,会触发内核中相应的回调函数ep_poll_callback()把事件信息添加到双向链表rdllist中。而调用epoll_wait()检测事件时只需要检查rdllist双向链表中是否有注册事件即可,极大的减少了CPU的占用率。

(3) epoll_wait()工作流程
1. epoll_wait调用内核的ep_poll检测rdlist链表是否为空如为空就挂起当前进程直到设定的时间到达,在这期间有事件添加进去就会唤醒进程继续工作。
2. 当文件描述符fd发生改变,与fd相应的回调函数ep_poll_callback会将fd对应的epitem加入到rdlist,而rdlist不为空时就会唤醒epoll_wait对应的进程继续工作。
3. 当进程唤醒后会调用内核的ep_events_transfer函数将rdlist链表中的epitem拷贝到txlist并清空rdlist。
4. 关键函数ep_send_events会扫描txlist链表中的每一个epitem,并调用fd对应的poll函数获取fd上最新的events,再将events对应的fd发送到用户空间。

一款简单Epoll+线程池类型的Web服务器

该代码只能实现简单web功能仅供参考

具体代码

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值