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(转)相关推荐

  1. Linux 下 I/O 多路复用技术 epoll

    Linux 下 I/O 多路复用技术 epoll 流?I/O操作?阻塞? 解决阻塞死等待的方法 方法一:阻塞 + 多进程 / 多线程. 方法二:非阻塞 + 忙轮询. 方法三:select(与平台无关) ...

  2. 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

  3. linux IO多路复用 select epoll

    概念 IO多路复用是指内核一旦发现进程指定的一个或者多个IO条件准备读取,它就通知该进程 通俗理解(摘自网上一大神) 这些名词比较绕口,理解涵义就好.一个epoll场景:一个酒吧服务员(一个线程),前 ...

  4. IO多路复用之epoll总结

    1.基本知识 epoll是在2.6内核中提出的,是之前的select和poll的增强版本.相对于select和poll来说,epoll更加灵活,没有描述符限制.epoll使用一个文件描述符管理多个描述 ...

  5. python网络编程——IO多路复用之epoll

    什么是epoll epoll是什么?在linux的网络编程中,很长的时间都在使用select来做事件触发.在linux新的内核中,有了一种替换它的机制,就是epoll.当然,这不是2.6内核才有的,它 ...

  6. 【Linux系统编程】IO多路复用之epoll

    00. 目录 文章目录 00. 目录 01. 概述 02. epoll函数 03. 程序示例 04. epoll优缺点 05. 附录 01. 概述 epoll是Linux下多路复用IO接口select ...

  7. Socket IO多路复用: epoll原理图解

    目录 一.accept 创建新 socket 1.1 初始化 struct socket 对象 1.2 为新 socket 对象申请 file 1.3 接收连接 1.4 添加新文件到当前进程的打开文件 ...

  8. 多路复用之——epoll

    什么是epoll epoll 与 select,poll 一样,其本质目的都是为了实现IO多路复用,将对多个文件描述符的等待时间重叠,提高 IO 的效率.但 epoll 不同的是,epoll 几乎解决 ...

  9. IO多路复用之epoll模型

    一.IO多路复用:一个线程监测多个IO操作 基本思想:先构造一张有关描述符的表,然后调用一个函数,当这些文件描述符中的一个或多个已经准备好进行I/O函数时才返回.函数返回时告诉进程哪个描述符已经就绪, ...

  10. IO多路复用机制——epoll

    高效地对海量用户提供服务,必须要让进程能同时处理很多个tcp连接.假设一个进程保持了10000条连接,如何发现哪条连接上有数据可读.可写? 实现:循环遍历来发现IO事件?效率太低了. 目录 IO模型 ...

最新文章

  1. not syncing : corrupted stack end detected inside scheduler解决办法 以及高版本的激活码!
  2. eclipse没有日志_IPFS技术最新进展:抵抗eclipse攻击的能力
  3. inline-block清除空隙2
  4. oracle级联删除表空间,Oracle表的创建.表空间创建删除,导入导出等
  5. 飞鸽传书10048错误的解决
  6. 重磅综述:三万字长文读懂单细胞RNA测序分析的最佳实践教程 (原理、代码和评述)
  7. mac os cmake安装
  8. 编程修养 阅读笔记二
  9. 《HBase权威指南》学习总结
  10. 国内优秀的PHP后台管理系统(CMS)整理
  11. 消息队列简介-原理与应用
  12. 在Vue项目中使用阿里巴巴矢量图
  13. Android黑白照片上色APP,黑白图片上色工具
  14. Java HashMap底层实现和原理分析(一)
  15. ThingWorx入门
  16. 无脑安装教程:windows 10—QT5.9.5 + vs2013
  17. linux signal
  18. 【名词】DAU和MAU
  19. 你对贝叶斯统计都有怎样的理解?
  20. 每日一道leetcode(python)695. 岛屿的最大面积

热门文章

  1. 关于ssh远程登录时出现问题的解决方法
  2. 没有传说,也没有神话,我只相信我自己
  3. windows server 2003 DNS 细谈系列之(二)记录类型、数据库
  4. Ubuntu16.04中WPS不能输入中文
  5. php采集列表xml代码,php读取xml列表程序
  6. 计算机主机组装的过程,电脑主机组装需要怎样的操作流程简单至极?
  7. TinyOS下TOSSIM仿真
  8. opencv-python图像处理之素描
  9. 谨慎的Waymo CEO:未来几十年,自动驾驶无法做到无处不在
  10. centos7 mariadb mysql max_connections=214 无法修改的问题