I/O多路复用之epoll(转)
4、epoll
在linux的网络编程中,很长的一段时间都在使用select来做事件触发。然而select逐渐暴露出了一些缺陷,使得linux不得不在新的内核中寻找出替代方案,那就是epoll。其实,epoll与select原理类似,只不过,epoll作出了一些重大改进,即:
a、当它们所监听的集合中有状态发生改变时,select需要循环检查整个集合,才能确定那个文件描述符状态发生改变,进而进行操作;而epoll在添加文件描述符到集合时,已经绑定了该文件描述符的对应函数,因此,当该文件描述符状态改变时,不需要循环查询整个集合,因而将复杂度由0(n)将为o(1),性能得到几何量级的提高,尤其是在大量连接的情况下。
b、再有,select所监听的描述字最大数目是有一定限制的,由FD_SETSIZE设置,通常是1024。对于那些需要支持的上万连接数目的web服务器来说显然是太少了,尽管可以通过修改头文件再重编译内核来扩大这个数目,不过资料也同时指出这样会带来网络效率的下降。(详细请参考:epoll 相对于poll的优点)
epoll的接口非常简单,一共就三个函数:
(1)、int epoll_create(int size);
创建一个epoll的句柄,size用来告诉内核这个监听的数目一共有多大。需要注意的是,当创建好epoll句柄后,epoll本身就占用一个fd值,所以用完后必须调用close()关闭,以防止fd被耗尽。
(2)、int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
epoll的事件注册函数,第一个参数是epoll_create()的返回值,第二个参数表示动作,用三个宏来表示:
EPOLL_CTL_ADD:注册新的fd到epfd中;
EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
EPOLL_CTL_DEL:从epfd中删除一个fd;
第三个参数是需要监听的fd,第四个参数是告诉内核需要监听什么事件,struct epoll_event结构如下:
struct epoll_event{
__uint32_t events; //epoll events
epoll_data_t data; //user data variable
};
events可以是以下几个宏的集合:
EPOLLIN:表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
EPOLLOUT:表示对应的文件描述符可以写;
EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
EPOLLERR:表示对应的文件描述符发生错误;
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET:将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。
EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加
入到EPOLL队列里。
(3)、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表示已超时。
令人高兴的是,2.6内核的epoll比其2.5开发版本的/dev/epoll简洁了许多,所以,大部分情况下,强大的东西往往是简单的。唯一有点麻烦是epoll有2种工作方式:LT和ET。
LT(level triggered)是缺省的工作方式,并且同时支持block和no-blocksocket.在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。如果你不作任何操作,内核还是会继续通知你的,所以,这种模式编程出错误可能性要小一点。传统的select/poll都是这种模型的代表.
ET (edge-triggered)是高速工作方式,只支持no-block socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了(比如,你在发送,接收或者接收请求,或者发送接收的数据少于一定量时导致了一个EWOULDBLOCK 错误)。但是请注意,如果一直不对这个fd作IO操作(从而导致它再次变成未就绪),内核不会发送更多的通知(only once),不过在TCP协议中,ET模式的加速效用仍需要更多的benchmark确认。
注:以上参考epoll精髓一文。
一个epoll的例子:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <sys/time.h>
#include <sys/resource.h>#define MAXBUF 1024
#define MAXEPOLLSIZE 10000/*
setnonblocking - 设置句柄为非阻塞方式
*/
int setnonblocking(int sockfd)
{if (fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFD, 0)|O_NONBLOCK) == -1) {return -1;}return 0;
}/*
handle_message - 处理每个 socket 上的消息收发
*/
int handle_message(int new_fd)
{char buf[MAXBUF + 1];int len;/* 开始处理每个新连接上的数据收发 */bzero(buf, MAXBUF + 1);/* 接收客户端的消息 */len = recv(new_fd, buf, MAXBUF, 0);if (len > 0)printf("%d接收消息成功:'%s',共%d个字节的数据\n",new_fd, buf, len);else {if (len < 0)printf("消息接收失败!错误代码是%d,错误信息是'%s'\n",errno, strerror(errno));close(new_fd);return -1;}/* 处理每个新连接上的数据收发结束 */return len;
}
/************关于本文档********************************************
*filename: epoll-server.c
*purpose: 演示epoll处理海量socket连接的方法
*wrote by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)
Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言
*date time:2007-01-31 21:00
*Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途
* 但请遵循GPL
*Thanks to:Google
*Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力
* 科技站在巨人的肩膀上进步更快!感谢有开源前辈的贡献!
*********************************************************************/
int main(int argc, char **argv)
{int listener, new_fd, kdpfd, nfds, n, ret, curfds;socklen_t len;struct sockaddr_in my_addr, their_addr;unsigned int myport, lisnum;struct epoll_event ev;struct epoll_event events[MAXEPOLLSIZE];struct rlimit rt;if (argv[1])myport = atoi(argv[1]);elsemyport = 7838;if (argv[2])lisnum = atoi(argv[2]);elselisnum = 2;/* 设置每个进程允许打开的最大文件数 */rt.rlim_max = rt.rlim_cur = MAXEPOLLSIZE;if (setrlimit(RLIMIT_NOFILE, &rt) == -1) {perror("setrlimit");exit(1);}else printf("设置系统资源参数成功!\n");/* 开启 socket 监听 */if ((listener = socket(PF_INET, SOCK_STREAM, 0)) == -1) {perror("socket");exit(1);} elseprintf("socket 创建成功!\n");setnonblocking(listener);bzero(&my_addr, sizeof(my_addr));my_addr.sin_family = PF_INET;my_addr.sin_port = htons(myport);if (argv[3])my_addr.sin_addr.s_addr = inet_addr(argv[3]);elsemy_addr.sin_addr.s_addr = INADDR_ANY;if (bind(listener, (struct sockaddr *) &my_addr, sizeof(struct sockaddr))== -1) {perror("bind");exit(1);} elseprintf("IP 地址和端口绑定成功\n");if (listen(listener, lisnum) == -1) {perror("listen");exit(1);} elseprintf("开启服务成功!\n");/* 创建 epoll 句柄,把监听 socket 加入到 epoll 集合里 */kdpfd = epoll_create(MAXEPOLLSIZE);len = sizeof(struct sockaddr_in);ev.events = EPOLLIN | EPOLLET;ev.data.fd = listener;if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, listener, &ev) < 0) {fprintf(stderr, "epoll set insertion error: fd=%d\n", listener);return -1;} elseprintf("监听 socket 加入 epoll 成功!\n");curfds = 1;while (1) {/* 等待有事件发生 */nfds = epoll_wait(kdpfd, events, curfds, -1);if (nfds == -1) {perror("epoll_wait");break;}/* 处理所有事件 */for (n = 0; n < nfds; ++n) {if (events[n].data.fd == listener) {new_fd = accept(listener, (struct sockaddr *) &their_addr,&len);if (new_fd < 0) {perror("accept");continue;} elseprintf("有连接来自于: %s:%d, 分配的 socket 为:%d\n", inet_ntoa(their_addr.sin_addr), ntohs(their_addr.sin_port), new_fd);setnonblocking(new_fd);ev.events = EPOLLIN | EPOLLET;ev.data.fd = new_fd;if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, new_fd, &ev) < 0) {fprintf(stderr, "把 socket '%d' 加入 epoll 失败!%s\n",new_fd, strerror(errno));return -1;}curfds++;} else {ret = handle_message(events[n].data.fd);if (ret < 1 && errno != 11) {epoll_ctl(kdpfd, EPOLL_CTL_DEL, events[n].data.fd,&ev);curfds--;}}}}close(listener);return 0;
}
编译此程序用命令:
gcc -Wall epoll-server.c -o server
运行此程序需要具有管理员权限!
sudo ./server 7838 1
通过测试这一个服务器可能同时处理10000 -3 = 9997 个连接!
如果这是一个在线服务系统,那么它可以支持9997人同时在线,比如游戏、聊天等。
转载于:https://www.cnblogs.com/czclaire/p/4897261.html
I/O多路复用之epoll(转)相关推荐
- Linux 下 I/O 多路复用技术 epoll
Linux 下 I/O 多路复用技术 epoll 流?I/O操作?阻塞? 解决阻塞死等待的方法 方法一:阻塞 + 多进程 / 多线程. 方法二:非阻塞 + 忙轮询. 方法三:select(与平台无关) ...
- IO多路复用之epoll总结 http://www.cnblogs.com/Anker/archive/2013/08/17/3263780.html
IO多路复用之epoll总结 http://www.cnblogs.com/Anker/archive/2013/08/17/3263780.html
- linux IO多路复用 select epoll
概念 IO多路复用是指内核一旦发现进程指定的一个或者多个IO条件准备读取,它就通知该进程 通俗理解(摘自网上一大神) 这些名词比较绕口,理解涵义就好.一个epoll场景:一个酒吧服务员(一个线程),前 ...
- IO多路复用之epoll总结
1.基本知识 epoll是在2.6内核中提出的,是之前的select和poll的增强版本.相对于select和poll来说,epoll更加灵活,没有描述符限制.epoll使用一个文件描述符管理多个描述 ...
- python网络编程——IO多路复用之epoll
什么是epoll epoll是什么?在linux的网络编程中,很长的时间都在使用select来做事件触发.在linux新的内核中,有了一种替换它的机制,就是epoll.当然,这不是2.6内核才有的,它 ...
- 【Linux系统编程】IO多路复用之epoll
00. 目录 文章目录 00. 目录 01. 概述 02. epoll函数 03. 程序示例 04. epoll优缺点 05. 附录 01. 概述 epoll是Linux下多路复用IO接口select ...
- Socket IO多路复用: epoll原理图解
目录 一.accept 创建新 socket 1.1 初始化 struct socket 对象 1.2 为新 socket 对象申请 file 1.3 接收连接 1.4 添加新文件到当前进程的打开文件 ...
- 多路复用之——epoll
什么是epoll epoll 与 select,poll 一样,其本质目的都是为了实现IO多路复用,将对多个文件描述符的等待时间重叠,提高 IO 的效率.但 epoll 不同的是,epoll 几乎解决 ...
- IO多路复用之epoll模型
一.IO多路复用:一个线程监测多个IO操作 基本思想:先构造一张有关描述符的表,然后调用一个函数,当这些文件描述符中的一个或多个已经准备好进行I/O函数时才返回.函数返回时告诉进程哪个描述符已经就绪, ...
- IO多路复用机制——epoll
高效地对海量用户提供服务,必须要让进程能同时处理很多个tcp连接.假设一个进程保持了10000条连接,如何发现哪条连接上有数据可读.可写? 实现:循环遍历来发现IO事件?效率太低了. 目录 IO模型 ...
最新文章
- not syncing : corrupted stack end detected inside scheduler解决办法 以及高版本的激活码!
- eclipse没有日志_IPFS技术最新进展:抵抗eclipse攻击的能力
- inline-block清除空隙2
- oracle级联删除表空间,Oracle表的创建.表空间创建删除,导入导出等
- 飞鸽传书10048错误的解决
- 重磅综述:三万字长文读懂单细胞RNA测序分析的最佳实践教程 (原理、代码和评述)
- mac os cmake安装
- 编程修养 阅读笔记二
- 《HBase权威指南》学习总结
- 国内优秀的PHP后台管理系统(CMS)整理
- 消息队列简介-原理与应用
- 在Vue项目中使用阿里巴巴矢量图
- Android黑白照片上色APP,黑白图片上色工具
- Java HashMap底层实现和原理分析(一)
- ThingWorx入门
- 无脑安装教程:windows 10—QT5.9.5 + vs2013
- linux signal
- 【名词】DAU和MAU
- 你对贝叶斯统计都有怎样的理解?
- 每日一道leetcode(python)695. 岛屿的最大面积
热门文章
- 关于ssh远程登录时出现问题的解决方法
- 没有传说,也没有神话,我只相信我自己
- windows server 2003 DNS 细谈系列之(二)记录类型、数据库
- Ubuntu16.04中WPS不能输入中文
- php采集列表xml代码,php读取xml列表程序
- 计算机主机组装的过程,电脑主机组装需要怎样的操作流程简单至极?
- TinyOS下TOSSIM仿真
- opencv-python图像处理之素描
- 谨慎的Waymo CEO:未来几十年,自动驾驶无法做到无处不在
- centos7 mariadb mysql max_connections=214 无法修改的问题