`

Linux Epoll介绍和程序实例

 
阅读更多

1. Epoll是何方神圣?

Epoll可是当前在Linux下开发大规模并发网络程序的热门人选,Epoll在Linux2.6内核中正式引入,和select相似,其实都I/O多路复用技术而已,并没有什么神秘的。

其实在Linux下设计并发网络程序,向来不缺少方法,比如典型的Apache模型(Process Per Connection,简称PPC),TPC(Thread Per Connection)模型,以及select模型和poll模型,那为何还要再引入Epoll这个东东呢?那还是有得说说的…

2.常用模型的缺点

如果不摆出来其他模型的缺点,怎么能对比出Epoll的优点呢。

2.1 PPC/TPC模型

这两种模型思想类似,就是让每一个到来的连接一边自己做事去,别再来烦我。只是PPC是为它开了一个进程,而TPC开了一个线程。可是别烦我是有代价的,它要时间和空间啊,连接多了之后,那么多的进程/线程切换,这开销就上来了;因此这类模型能接受的最大连接数都不会高,一般在几百个左右。

2.2 select模型

1.最大并发数限制,因为一个进程所打开的FD(文件描述符)是有限制的,由FD_SETSIZE设置,默认值是1024/2048,因此Select模型的最大并发数就被相应限制了。自己改改这个FD_SETSIZE?想法虽好,可是先看看下面吧…

2.效率问题,select每次调用都会线性扫描全部的FD集合,这样效率就会呈现线性下降,把FD_SETSIZE改大的后果就是,大家都慢慢来,什么?都超时了??!!

3.内核/用户空间内存拷贝问题,如何让内核把FD消息通知给用户空间呢?在这个问题上select采取了内存拷贝方法。

2.3 poll模型

基本上效率和select是相同的,select缺点的2和3它都没有改掉。

3. Epoll的提升

把其他模型逐个批判了一下,再来看看Epoll的改进之处吧,其实把select的缺点反过来那就是Epoll的优点了。

3.1. Epoll没有最大并发连接的限制,上限是最大可以打开文件的数目,这个数字一般远大于2048,一般来说这个数目和系统内存关系很大,具体数目可以cat /proc/sys/fs/file-max察看。

3.2.效率提升,Epoll最大的优点就在于它只管你“活跃”的连接,而跟连接总数无关,因此在实际的网络环境中,Epoll的效率就会远远高于select和poll。

3.3.内存拷贝,Epoll在这点上使用了“共享内存”,这个内存拷贝也省略了。

 

4. Epoll为什么高效

Epoll的高效和其数据结构的设计是密不可分的,这个下面就会提到。

首先回忆一下select模型,当有I/O事件到来时,select通知应用程序有事件到了快去处理,而应用程序必须轮询所有的FD集合,测试每个FD是否有事件发生,并处理事件;代码像下面这样:

 

intres = select(maxfd+1, &readfds, NULL, NULL, 120);

if(res > 0)

{

for(inti = 0; i < MAX_CONNECTION; i++)

{

if(FD_ISSET(allConnection[i], &readfds))

{

handleEvent(allConnection[i]);

}

}

}

// if(res == 0) handle timeout, res < 0 handle error

 

Epoll不仅会告诉应用程序有I/0事件到来,还会告诉应用程序相关的信息,这些信息是应用程序填充的,因此根据这些信息应用程序就能直接定位到事件,而不必遍历整个FD集合。

intres = epoll_wait(epfd, events, 20, 120);

for(inti = 0; i < res;i++)

{

handleEvent(events[n]);

}

5. Epoll关键数据结构

前面提到Epoll速度快和其数据结构密不可分,其关键数据结构就是:

structepoll_event {

__uint32_t events;// Epoll events

epoll_data_t data;// User data variable

};

typedefunionepoll_data {

void*ptr;

intfd;

__uint32_t u32;

__uint64_t u64;

} epoll_data_t;

可见epoll_data是一个union结构体,借助于它应用程序可以保存很多类型的信息:fd、指针等等。有了它,应用程序就可以直接定位目标了。

6.使用Epoll

既然Epoll相比select这么好,那么用起来如何呢?会不会很繁琐啊…先看看下面的三个函数吧,就知道Epoll的易用了。

 

intepoll_create(intsize);

生成一个Epoll专用的文件描述符,其实是申请一个内核空间,用来存放你想关注的socket fd上是否发生以及发生了什么事件。size就是你在这个Epoll fd上能关注的最大socket fd数,大小自定,只要内存足够。

intepoll_ctl(intepfd,intop,intfd,structepoll_event *event);

控制某个Epoll文件描述符上的事件:注册、修改、删除。其中参数epfd是epoll_create()创建Epoll专用的文件描述符。相对于select模型中的FD_SET和FD_CLR宏。

intepoll_wait(intepfd,structepoll_event * events,intmaxevents,inttimeout);

等待I/O事件的发生;参数说明:

epfd:由epoll_create()生成的Epoll专用的文件描述符;

epoll_event:用于回传代处理事件的数组;

maxevents:每次能处理的事件数;

timeout:等待I/O事件发生的超时值;

返回发生事件数。

相对于select模型中的select函数。

7.例子程序

下面是一个简单Echo Server的例子程序,麻雀虽小,五脏俱全,还包含了一个简单的超时检查机制,简洁起见没有做错误处理。

 

  1. //
  2. //asimpleechoserverusingepollinlinux
  3. //
  4. //2009-11-05
  5. //bysparkling
  6. //
  7. #include<sys/socket.h>
  8. #include<sys/epoll.h>
  9. #include<netinet/in.h>
  10. #include<arpa/inet.h>
  11. #include<fcntl.h>
  12. #include<unistd.h>
  13. #include<stdio.h>
  14. #include<errno.h>
  15. #include<iostream>
  16. usingnamespacestd;
  17. #defineMAX_EVENTS500
  18. structmyevent_s
  19. {
  20. intfd;
  21. void(*call_back)(intfd,intevents,void*arg);
  22. intevents;
  23. void*arg;
  24. intstatus;//1:inepollwaitlist,0notin
  25. charbuff[128];//recvdatabuffer
  26. intlen;
  27. longlast_active;//lastactivetime
  28. };
  29. //setevent
  30. voidEventSet(myevent_s*ev,intfd,void(*call_back)(int,int,void*),void*arg)
  31. {
  32. ev->fd=fd;
  33. ev->call_back=call_back;
  34. ev->events=0;
  35. ev->arg=arg;
  36. ev->status=0;
  37. ev->last_active=time(NULL);
  38. }
  39. //add/modaneventtoepoll
  40. voidEventAdd(intepollFd,intevents,myevent_s*ev)
  41. {
  42. structepoll_eventepv={0,{0}};
  43. intop;
  44. epv.data.ptr=ev;
  45. epv.events=ev->events=events;
  46. if(ev->status==1){
  47. op=EPOLL_CTL_MOD;
  48. }
  49. else{
  50. op=EPOLL_CTL_ADD;
  51. ev->status=1;
  52. }
  53. if(epoll_ctl(epollFd,op,ev->fd,&epv)<0)
  54. printf("EventAddfailed[fd=%d]/n",ev->fd);
  55. else
  56. printf("EventAddOK[fd=%d]/n",ev->fd);
  57. }
  58. //deleteaneventfromepoll
  59. voidEventDel(intepollFd,myevent_s*ev)
  60. {
  61. structepoll_eventepv={0,{0}};
  62. if(ev->status!=1)return;
  63. epv.data.ptr=ev;
  64. ev->status=0;
  65. epoll_ctl(epollFd,EPOLL_CTL_DEL,ev->fd,&epv);
  66. }
  67. intg_epollFd;
  68. myevent_sg_Events[MAX_EVENTS+1];//g_Events[MAX_EVENTS]isusedbylistenfd
  69. voidRecvData(intfd,intevents,void*arg);
  70. voidSendData(intfd,intevents,void*arg);
  71. //acceptnewconnectionsfromclients
  72. voidAcceptConn(intfd,intevents,void*arg)
  73. {
  74. structsockaddr_insin;
  75. socklen_tlen=sizeof(structsockaddr_in);
  76. intnfd,i;
  77. //accept
  78. if((nfd=accept(fd,(structsockaddr*)&sin,&len))==-1)
  79. {
  80. if(errno!=EAGAIN&&errno!=EINTR)
  81. {
  82. printf("%s:badaccept",__func__);
  83. }
  84. return;
  85. }
  86. do
  87. {
  88. for(i=0;i<MAX_EVENTS;i++)
  89. {
  90. if(g_Events[i].status==0)
  91. {
  92. break;
  93. }
  94. }
  95. if(i==MAX_EVENTS)
  96. {
  97. printf("%s:maxconnectionlimit[%d].",__func__,MAX_EVENTS);
  98. break;
  99. }
  100. //setnonblocking
  101. if(fcntl(nfd,F_SETFL,O_NONBLOCK)<0)break;
  102. //addareadeventforreceivedata
  103. EventSet(&g_Events[i],nfd,RecvData,&g_Events[i]);
  104. EventAdd(g_epollFd,EPOLLIN|EPOLLET,&g_Events[i]);
  105. printf("newconn[%s:%d][time:%d]/n",inet_ntoa(sin.sin_addr),ntohs(sin.sin_port),g_Events[i].last_active);
  106. }while(0);
  107. }
  108. //receivedata
  109. voidRecvData(intfd,intevents,void*arg)
  110. {
  111. structmyevent_s*ev=(structmyevent_s*)arg;
  112. intlen;
  113. //receivedata
  114. len=recv(fd,ev->buff,sizeof(ev->buff)-1,0);
  115. EventDel(g_epollFd,ev);
  116. if(len>0)
  117. {
  118. ev->len=len;
  119. ev->buff[len]='/0';
  120. printf("C[%d]:%s/n",fd,ev->buff);
  121. //changetosendevent
  122. EventSet(ev,fd,SendData,ev);
  123. EventAdd(g_epollFd,EPOLLOUT|EPOLLET,ev);
  124. }
  125. elseif(len==0)
  126. {
  127. close(ev->fd);
  128. printf("[fd=%d]closedgracefully./n",fd);
  129. }
  130. else
  131. {
  132. close(ev->fd);
  133. printf("recv[fd=%d]error[%d]:%s/n",fd,errno,strerror(errno));
  134. }
  135. }
  136. //senddata
  137. voidSendData(intfd,intevents,void*arg)
  138. {
  139. structmyevent_s*ev=(structmyevent_s*)arg;
  140. intlen;
  141. //senddata
  142. len=send(fd,ev->buff,ev->len,0);
  143. ev->len=0;
  144. EventDel(g_epollFd,ev);
  145. if(len>0)
  146. {
  147. //changetoreceiveevent
  148. EventSet(ev,fd,RecvData,ev);
  149. EventAdd(g_epollFd,EPOLLIN|EPOLLET,ev);
  150. }
  151. else
  152. {
  153. close(ev->fd);
  154. printf("recv[fd=%d]error[%d]/n",fd,errno);
  155. }
  156. }
  157. voidInitListenSocket(intepollFd,shortport)
  158. {
  159. intlistenFd=socket(AF_INET,SOCK_STREAM,0);
  160. fcntl(listenFd,F_SETFL,O_NONBLOCK);//setnon-blocking
  161. printf("serverlistenfd=%d/n",listenFd);
  162. EventSet(&g_Events[MAX_EVENTS],listenFd,AcceptConn,&g_Events[MAX_EVENTS]);
  163. //addlistensocket
  164. EventAdd(epollFd,EPOLLIN|EPOLLET,&g_Events[MAX_EVENTS]);
  165. //bind&listen
  166. sockaddr_insin;
  167. bzero(&sin,sizeof(sin));
  168. sin.sin_family=AF_INET;
  169. sin.sin_addr.s_addr=INADDR_ANY;
  170. sin.sin_port=htons(port);
  171. bind(listenFd,(constsockaddr*)&sin,sizeof(sin));
  172. listen(listenFd,5);
  173. }
  174. intmain(intargc,char**argv)
  175. {
  176. shortport=12345;//defaultport
  177. if(argc==2){
  178. port=atoi(argv[1]);
  179. }
  180. //createepoll
  181. g_epollFd=epoll_create(MAX_EVENTS);
  182. if(g_epollFd<=0)printf("createepollfailed.%d/n",g_epollFd);
  183. //create&bindlistensocket,andaddtoepoll,setnon-blocking
  184. InitListenSocket(g_epollFd,port);
  185. //eventloop
  186. structepoll_eventevents[MAX_EVENTS];
  187. printf("serverrunning:port[%d]/n",port);
  188. intcheckPos=0;
  189. while(1){
  190. //asimpletimeoutcheckhere,everytime100,bettertouseamini-heap,andaddtimerevent
  191. longnow=time(NULL);
  192. for(inti=0;i<100;i++,checkPos++)//doesn'tchecklistenfd
  193. {
  194. if(checkPos==MAX_EVENTS)checkPos=0;//recycle
  195. if(g_Events[checkPos].status!=1)continue;
  196. longduration=now-g_Events[checkPos].last_active;
  197. if(duration>=60)//60stimeout
  198. {
  199. close(g_Events[checkPos].fd);
  200. printf("[fd=%d]timeout[%d--%d]./n",g_Events[checkPos].fd,g_Events[checkPos].last_active,now);
  201. EventDel(g_epollFd,&g_Events[checkPos]);
  202. }
  203. }
  204. //waitforeventstohappen
  205. intfds=epoll_wait(g_epollFd,events,MAX_EVENTS,1000);
  206. if(fds<0){
  207. printf("epoll_waiterror,exit/n");
  208. break;
  209. }
  210. for(inti=0;i<fds;i++){
  211. myevent_s*ev=(structmyevent_s*)events[i].data.ptr;
  212. if((events[i].events&EPOLLIN)&&(ev->events&EPOLLIN))//readevent
  213. {
  214. ev->call_back(ev->fd,events[i].events,ev->arg);
  215. }
  216. if((events[i].events&EPOLLOUT)&&(ev->events&EPOLLOUT))//writeevent
  217. {
  218. ev->call_back(ev->fd,events[i].events,ev->arg);
  219. }
  220. }
  221. }
  222. //freeresource
  223. return0;
  224. }
分享到:
评论

相关推荐

    Linux_Epoll介绍和程序实例

    Linux_Epoll介绍和程序实例,帮你了解EPOLL编程

    Linux_Epoll介绍和程序实例.doc

    Linux_Epoll介绍和程序实例.doc

    linux下的epoll服务程序实例

    linux下的epoll服务程序及客户端实例,服务程序支持多连接,并能稳定进行数据传输。

    linux下epoll示例程序

    linux下epoll示例程序 服务器端 客服端 实现多人聊天

    linux下Epoll模型实例代码

    linux下简单的epoll模型的例子程序

    epoll学习实例

    epoll学习实例,epoll是Linux内核为处理大批量文件描述符而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。另一点原因就是...

    Web服务器实例

    该程序为一个使用Linux epoll的小型web服务器实例,目前只能接受GET方法,不过可以扩展。

    Linux高性能服务器编程

    包含Linux网络编程API、高级I/O函数、Linux服务器程序规范、高性能服务器程序框架、I/O复用、信号、定时器、高性能I/O框架库Libevent、多进程编程、多线程编程、进程池和线程池等内容,原理、技术与方法并重;...

    Python的Tornado框架异步编程入门实例

    Torando 在Linux和FreeBSD上使用高效的异步I/O模型 epoll 和kqueue来实现高效的web服务器, 所以 tornado在Linux上和FreeBSD系列性能可以达到最高 接口 当然我们可以不仅仅把Tornado看作是一个web框架和web服务器, ...

    python入门到高级全栈工程师培训 第3期 附课件代码

    09 select与epoll的实现区别 第36章 01 异步IO 02 selectors模块介绍 03 selectors模块应用 04 作业介绍 第37章 01 selctors实现文件上传与下载 02 html的介绍 03 html文档树的概念 04 meta标签以及一些基本标签...

    实战Nginx:取代Apache的高性能Web服务器

    Nginx选择了epoll和kqueue作为网络I/O模型,在高连接并发的情况下,Nginx是Apache服务器不错的替代品,它能够支持高达50 000个并发连接数的响应,运行稳定,且内存、CPU等系统资源消耗非常低。  本书主要分为4个...

Global site tag (gtag.js) - Google Analytics