Linux epoll总结

Linux  epoll

  epoll是Kernel 2.6后新加入的事件机制,在高并发条件下,远优于select。epoll最大的好处在于它不会随着监听fd数目的增长而降低效率。因为在内核中的select实现中,它是采用轮询来处理的,轮询的fd数目越多,自然耗时越多。并且,在linux/posix_types.h头文件有这样的声明:

#define __FD_SETSIZE    1024 //select最多同时监听1024个fd

  当然,可以通过修改头文件再重编译内核来扩大这个数目,但这似乎并不治本。

  所以在Nginx中采用了epoll来实现其高并发特性。

工作方式

  LT(level triggered):水平触发,缺省方式,同时支持block和no-block socket,在这种做法中,内核告诉我们一个文件描述符是否被就绪了,如果就绪了,你就可以对这个就绪的fd进行IO操作。如果你不作任何操作,内核还是会继续通知你的,所以,这种模式编程出错的可能性较小。传统的select\poll都是这种模型的代表。

  ET(edge-triggered):边沿触发,高速工作方式,只支持no-block socket。在这种模式下,当描述符从未就绪变为就绪状态时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个描述符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了(比如:你在发送、接受或者接受请求,或者发送接受的数据少于一定量时导致了一个EWOULDBLOCK错误)。但是请注意,如果一直不对这个fs做IO操作(从而导致它再次变成未就绪状态),内核不会发送更多的通知。

  区别:LT事件不会丢弃,而是只要读buffer里面有数据可以让用户读取,则不断的通知你。而ET则只在事件发生之时通知。

主要的数据结构

  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 event */
epoll_data_t data; /* User data variable */
};

  events表示感兴趣的事件和被触发的事件,可能的取值为:

EPOLLIN 对应的文件描述符可以读
EPOLLOUT 对应的文件描述符可以写
EPOLLPRI 对应的文件描述符有紧急的数可读
EPOLLERR 对应的文件描述符发生错误
EPOLLHUP 对应的文件描述符被挂断
EPOLLET 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的
EPOLLONESHOT 只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里 

操作函数

   epoll的接口非常简单,用三个相关函数来创建epoll句柄、注册epoll事件以及等待事件的发生。

  创建epoll句柄:

int epoll_create(int size);
//size表示内核需要监听的数目
//return : epoll文件描述符

  需要注意的是,当创建好epoll句柄后,它就是会占用一个fd值(文件标识符),在linux下如果查看/proc/进程id/fd/,是能够看到这个fd的,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。

  epoll事件注册函数:

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)

//epfd是epoll_create()的返回值
//op表示动作
/* op可被表示为:
EPOLL_CTL_ADD:注册新的fd到epfd中;
EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
EPOLL_CTL_DEL:从epfd中删除一个fd;
*/
//fd是需要监听的fd
//event是内核需要监听的事件

  等待事件发生函数:

int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout)
//epfd是函数返回值
//events是内核监听事件的集合
//maxevents是epoll_wait可以处理的连接事件的最大限度值
//timeout是超时时间 //返回值:请求数

epoll工作流程

  首先,需要调用epoll_create创建epoll,此后我们就可以进行socket/bind/listen,然后调用epoll_ctl进行注册。接下来,就可以通过一个while(1)循环调用epoll_wait来等待事件的发生,然后循环查看接收到的事件并进行处理。如果事件是sever的socketfd我们就要进行accept,并且把接收到client的socketfd加入到要监听的事件中。如果在监听过程中,需要修改操作方式(读/写),可以调用epoll_ctl来重新修改。如果监听到某一个客户端关闭,那么我就需要再次调用epoll_ctl把它从epoll监听事件中删除。

实例

  1 //epoll_server.c
2 #include <stdio.h>
3 #include <string.h>
4 #include <sys/socket.h>
5 #include <sys/epoll.h>
6 #include <errno.h>
7 #include <netinet/in.h>
8 #include <arpa/inet.h>
9 #include <sys/types.h>
10 #include <unistd.h>
11
12 #define SERV_PORT 8802
13
14
15 int main()
16 {
17 int i,flag;
18 int sockfd,clntfd,newfd;
19 int epfd,nfds;
20 ssize_t n;
21 char buffer[1024];
22 int s = sizeof(struct sockaddr);
23
24 struct sockaddr_in serv_addr;
25 struct sockaddr_in clnt_addr;
26 //定义epoll数据结构
27 struct epoll_event ev,events[20];
28
29 epfd = epoll_create(256);
30
31 //创建socket,并初始化事件ev
32 sockfd = socket(AF_INET, SOCK_STREAM, 0);
33 if(sockfd < 0){
34 perror("socket error!\n");
35 return -1;
36 }
37 ev.data.fd = sockfd;
38 ev.events = EPOLLIN|EPOLLET;
39
40 //注册epoll事件
41 flag = epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &ev);
42 if(flag < 0){
43 perror("epoll_ctl error!\n");
44 return -1;
45 }
46
47 bzero(&serv_addr, sizeof(serv_addr));
48 serv_addr.sin_family = AF_INET;
49 serv_addr.sin_port = htons(SERV_PORT);
50 serv_addr.sin_addr.s_addr = htonl( INADDR_ANY );
51
52 flag = bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(struct sockaddr));
53 if(flag < 0){
54 perror("bind error!\n");
55 return -1;
56 }
57 printf("bind\n");
58
59 flag = listen(sockfd, 20);
60 if(flag < 0){
61 perror("listen error!\n");
62 return -1;
63 }
64 printf("listen\n");
65
66 //开始循环
67 while(1){
68 //等待事件发生,返回请求数目
69 nfds = epoll_wait(epfd, events, 20, 500);
70 //一次处理请求
71 for(i = 0; i < nfds; ++i){
72 if(events[i].data.fd == sockfd){
73 clntfd = accept(sockfd, (struct sockaddr*)&clnt_addr,(unsigned int*)&s);
74 if(clntfd < 0){
75 perror("accept error");
76 continue;
77 }
78 printf("accept\n");
79
80 char *str = inet_ntoa(clnt_addr.sin_addr);
81 printf("accepnt the client ip : %s\n",str);
82
83 //设置文件标识符,设置操作属性:写操作
84 ev.data.fd = clntfd;
85 ev.events = EPOLLOUT|EPOLLET;
86 //向创建的的epoll进行注册写操作
87 epoll_ctl(epfd, EPOLL_CTL_ADD, clntfd, &ev);
88 }
89 else if(events[i].events&EPOLLOUT){
90 printf("EPOLLOUT\n");
91
92 if((newfd = events[i].data.fd) < 0)
93 continue;
94 bzero(buffer,sizeof(buffer));
95 strcpy(buffer,"welcome to myserver!\n");
96 flag = send(newfd, buffer, 1024, 0);
97 if(flag < 0){
98 perror("send error");
99 continue;
100 }
101 //修改操作为读操作
102 ev.data.fd = clntfd;
103 ev.events = EPOLLIN|EPOLLET;
104 epoll_ctl(epfd, EPOLL_CTL_MOD, newfd, &ev);
105 }
106 else if(events[i].events&EPOLLIN){
107 printf("EPOLLIN\n");
108
109 bzero(buffer,sizeof(buffer));
110 if((newfd = events[i].data.fd) < 0)
111 continue;
112 if((n = read(newfd, buffer, 1024)) < 0){
113 if(errno == ECONNRESET){
114 close(newfd);
115 events[i].data.fd = -1;
116 printf("errno ECONRESET!\n");
117 }
118 else{
119 perror("readbuffer error!\n");
120 }
121 }
122 else if(n == 0){//表示客户端已经关闭
123 close(newfd);
124 events[i].data.fd = -1;
125 printf("n为0\n");
126 }
127 if(buffer[0]!='0')
128 printf("have read: %s\n",buffer);
129 }
130 }
131 }
132 close(sockfd);
133 return 0;
134 }

源码下载:这里

参考

  

http://www.cnblogs.com/venow/archive/2012/11/30/2790031.html

     http://hi.baidu.com/jingweiyoung/item/ae9fc81714be67dbbf9042b9

     http://www.linuxidc.com/Linux/2011-04/35156p3.htm

   http://www.cppblog.com/converse/archive/2008/04/29/48482.html

 
 
 

Linux epoll总结的更多相关文章

  1. Server Develop (六) Linux epoll总结

    Linux  epoll epoll是Kernel 2.6后新加入的事件机制,在高并发条件下,远优于select.epoll最大的好处在于它不会随着监听fd数目的增长而降低效率.因为在内核中的sele ...

  2. Linux Epoll介绍和程序实例

    Linux Epoll介绍和程序实例 1. Epoll是何方神圣? Epoll但是当前在Linux下开发大规模并发网络程序的热门人选,Epoll 在Linux2.6内核中正式引入,和select类似, ...

  3. c/c++ linux epoll系列3 利用epoll_wait设置timeout时间长度

    linux epoll系列3 利用epoll_wait设置timeout时间长度 epoll_wait函数的第四个参数可以设置,epoll_wait函数的等待时间(timeout时间长度). 例子1, ...

  4. c/c++ linux epoll系列2 利用epoll_wait查看是否可以送信

    linux epoll系列2 利用epoll_wait查看是否可以送信 write函数本来是非阻塞函数,但是当缓存区被写满后,再往缓存区里写的时候,就必须等待缓存区再次变成可写,所以这是write就变 ...

  5. c/c++ linux epoll系列1 创建epoll

    linux epoll系列1 创建epoll 据说select和poll的弱点是,随着连接(socket)的增加,性能会直线下降. epoll不会随着连接(socket)的增加,性能直线下降. 知识点 ...

  6. Windows完成端口与Linux epoll技术简介

    收藏自:http://www.cnblogs.com/cr0-3/archive/2011/09/09/2172280.html WINDOWS完成端口编程1.基本概念2.WINDOWS完成端口的特点 ...

  7. Java网络编程和NIO详解6:Linux epoll实现原理详解

    Java网络编程和NIO详解6:Linux epoll实现原理详解 本系列文章首发于我的个人博客:https://h2pl.github.io/ 欢迎阅览我的CSDN专栏:Java网络编程和NIO h ...

  8. 源码剖析Linux epoll实现机制及Linux上惊群

    转载:https://blog.csdn.net/tgxallen/article/details/78086360 看源码是对一个技术认识最直接且最有效的方式了,之前用Linux Epoll做过一个 ...

  9. Windows完成端口与Linux epoll技术简介(能看懂)

    WINDOWS完成端口编程1.基本概念2.WINDOWS完成端口的特点3.完成端口(Completion Ports )相关数据结构和创建4.完成端口线程的工作原理5.Windows完成端口的实例代码 ...

随机推荐

  1. spark in eclipse---Spark学习笔记3

    想要调试源码,还是要放到eclipse里面去.先生成eclipse项目,下载依赖包 victor@victor-ubuntu:~/software/incubator-spark-0.8.1-incu ...

  2. POJ 1201 &amp;&amp; HDU 1384 Intervals(差动制动系统)

    职务地址:POJ 1201   HDU 1384 依据题目意思.能够列出不等式例如以下: Sj-Si>=c; Si-S(i-1)>=0; S(i-1)-Si>=-1; 然后用最短路s ...

  3. java利用poi导出数据到excel

    背景: 上一篇写到利用jtds连接数据库获取对应的数据,本篇写怎样用poi将数据到处到excel中,此程序为Application 正文: 第三方poi jar包:poi驱动包下载 代码片段: /** ...

  4. JS基础——事件绑定

    上一篇博客JS事件对象中,老师问JS事件处理和VB中的事件处理有什么联系?先来解决一下这个问题.举个VB.net中事件处理的样例(JS敲久了,VB习惯的都不熟悉了,看来得常常回想了): 1.事件处理V ...

  5. SQL SERVER2005 excel float导入

    接到mission:将一堆excel的东西导入到SQL SERVER2005 命令很easy SELECT * INTO XLImport3 FROM OPENDATASOURCE('Microsof ...

  6. Singal Page App:使用Knockout和RequireJS创建高度模块化的单页应用引擎

    Singal Page App 开篇扯淡 距离上一篇文章已经有好几个月,也不是没有时间记录点东西,主要是换了新的工作,在一家外资工作,目前的工作内容大多都是前端开发,新接触的东西因为时间原因,大多还不 ...

  7. 网站开发常用jQuery插件总结(二)弹出层插件Lightbox

    网站开发过程中,为了增加网站交互效果,我们有时需要在当前页面弹出诸如登陆.注册.设置等窗口.而这些窗口就是层,弹出的窗口就是弹出层.jQuery中弹出层插件很多,但有些在html5+css3浏览器下, ...

  8. javascript 学习总结(三)Boolean对象

    Boolean对象 /* 创建 Boolean 对象的语法: new Boolean(value); //构造函数 Boolean(value); //转换函数 参数 value 由布尔对象存放的值或 ...

  9. .net微软消息队列(msmq)简单案例

    1.首先我们需要安装消息队列服务,它是独立的消息记录的服务,并保存在硬盘文件中. 我们添加名为:DMImgUpload的私有消息队列. 2.定义消息队列的连接字符串建议采用IP: (1)FormatN ...

  10. HttpTest4Net

    HttpTest4Net HttpTest4Net是一款基于C#实现的和HTTP压力测试工具,通过工具可以简单地对HTTP服务进行一个压力测试.虽然VS.NET也集成了压力测试项目,但由于VS自身占用 ...