文章目录
1. 为什么要使用epoll?
实现I/O复用的传统方法有select和poll,但这两者都无法实现同时接入上百个客户端,所以select并不适合当前以Web服务端开发为主流的现代开发环境,于是就产生了epoll。epoll是linux平台所支持的,在Windows平台对应支持的是IOCP。
1.1 基于select的I/O复用技术速度慢的原因
- 调用select函数后,需要对所有文件描述符执行循环检查语句。
- 每次调用select函数时都需要向该函数传递监视对象信息。
循环语句不是最大的麻烦,每次传递监视对象信息到操作系统中才是最大的麻烦。
应用程序向操作系统传递数据将对程序造成很大负担,而且无法通过优化代码解决,因此将成为性能上的致命弱点。
select函数与文件描述符有关,更准确地说,是监视套接字变化的函数。而套接字是由操作系统管理的,所以select函数绝对需要借助于操作系统才能完成功能。select函数的这一缺点可以通过如下方式弥补:
"仅向操作系统传递1 次监视对象,监视范围或内容发生变化时只通知发生变化的事项。”
这样就无需每次调用select函数时都向操作系统传递监视对象信息,但前提是操作系统支持这种处理方式(每种操作系统支持的程度和方式存在差异)。Linux的支待方式是epoll, Windows的支持方式是IOCP 。
select 的优点
epoll方式只在Linux下提供支持,也就是说,改进的I/O复用模型不具有兼容性。相反,大部分操作系统都支持select函数。只要满足或要求如下两个条件, 即使在Linux平台也不应拘泥于epoll 。
- 服务器端接入者少。
- 程序应具有兼容性。
2. epoll函数的介绍
epoll函数具有如下优点,这些优点正好与之前的select函数缺点相反。
- 无需编写以监视状态变化为目的的针对所有文件描述符的循环语句。
- 调用对应于select函数的epoll_wait函数时无需每次传递监视对象信息。
epoll服务器端实现中需要的3个函数,
- epoll_ create: 创建保存epoll文件描述符的空间。
- epoll_ctl: 向空间注册并注销文件描述符。
- epoll_wait: 与select函数类似,等待文件描述符发生变化。
select和epoll的不同
select方式
- 用fd_set变量保存监视对象文件描述符。
- 用FD_SET,FD_CLR添加和删除对象。
- select函数等待文件描述符的变化。
- 通过FD_ISSET查看状态变化的文件描述符。
epoll方式
- 操作系统负责保存监视对象文件描述符,因此需要向操作系统请求创建保存文件描述符的空间。
- 通过epoll_ctl 函数请求操作系统添加和删除对象。
- epoll_wait函数等待文件描述符的变化。
- 通过结构体epoll_event将发生变化的文件描述符单独集中到一起。
epoll_event结构体:
struct epoll_event
{
uint32_t events;
epoll_data_t data;
}
typedef union epoll_data
{
void * ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
声明足够大的epoll_even结构体数组后,传递给epoll_wait函数时.发生变化的文件描述符信息将被填入该数组。因此, 无需像select函数那样针对所有文件描述符进行循环。
2.1 epoll_create
#include <sys/epoll. h>
int epoll_create(int size);
/*
成功时返回epoll 文件描述符,失败时返回-1。
#size: epoll实例的大小。
*/
调用epoll_creat函数创建的文件描述符保存空间称为“epoll例程”,有些情况下名称会不同。通过传递size参数来决定空间大小,但这个值只是给操作系统一个建议,换言之,size并不能决定epoll的大小。
Linux 2.6.8 之后的内核将完全忽略传入epoll_create 函数的size 参数,因为内核会根据情况调整epoll 例程的大小。
epoll_creat函数创建的资源与套接字相同,也由操作系统管理。因此,此函数和创建套接字的情况相同,也会返回文件描述符。需要终止时,与其他文件描述符相同,也要调用close函数。
2.2 epoll_ctl
生成例程后,应在其内部注册监视对象文件描述符,此时使用epoll_ctl函数。
#include <sys/epoll. h>
int epoll_ctl(int epfd, int op, int fd, struct epoll_event * event);
//成功时返回0 , 失败时返回-1 。
#epfd 用千注册监视对象的epoll例程的文件描述符。
#op 用千指定监视对象的添加、删除或更改等操作。
#fd 需要注册的监视对象文件描述符。
#event 监视对象的事件类型。
该函数看起来复杂,但是调用语句就很容易理解,假设按如下形式调用epoll_ctl函数:
epoll_ctl(A, EPOLL_CTL_ADD, B, C);
第二个参数 EPOLL_CTL_A