网络模型,Epoll介绍,和几种其他模型的比较! 我不是女神ヾ 2023-01-17 03:11 2阅读 0赞 Linux Epoll介绍和程序实例 1. Epoll是何方神圣? Epoll可是当前在Linux下开发大规模并发网络程序的热门人选,Epoll 在Linux2.6内核中正式引入,和select相似,其实都**I/O多路复用技术而已**,并没有什么神秘的。 其实在Linux下设计并发网络程序,向来不缺少方法,比如典型的Apache模型(Process Per Connection,简称PPC),TPC(Thread PerConnection)模型,以及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是否有事件发生,并处理事件;代码像下面这样: int res = select(maxfd+1, &readfds, NULL, NULL, 120); if(res > 0) \{ for(int i = 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(int i = 0; i < res;i++) \{ handleEvent(events\[n\]); \} 5. Epoll关键数据结构 前面提到Epoll速度快和其数据结构密不可分,其关键数据结构就是: structepoll\_event \{ \_\_uint32\_t events; // Epoll events epoll\_data\_t data; // User datavariable \}; typedefunion epoll\_data \{ void \*ptr; int fd; \_\_uint32\_t u32; \_\_uint64\_t u64; \} epoll\_data\_t; 可见epoll\_data是一个union结构体,借助于它应用程序可以保存很多类型的信息:fd、指针等等。有了它,应用程序就可以直接定位目标了。 6. 使用Epoll 既然Epoll相比select这么好,那么用起来如何呢?会不会很繁琐啊…先看看下面的三个函数吧,就知道Epoll的易用了。 intepoll\_create(int size); 生成一个Epoll专用的文件描述符,其实是申请一个内核空间,用来存放你想关注的socket fd上是否发生以及发生了什么事件。size就是你在这个Epoll fd上能关注的最大socket fd数,大小自定,只要内存足够。 intepoll\_ctl(int epfd, intop, int fd, structepoll\_event \*event); 控制某个Epoll文件描述符上的事件:注册、修改、删除。其中参数epfd是epoll\_create()创建Epoll专用的文件描述符。相对于select模型中的FD\_SET和FD\_CLR宏。 intepoll\_wait(int epfd,structepoll\_event \* events,int maxevents,int timeout); 等待I/O事件的发生;参数说明: epfd:由**epoll\_create()** 生成的Epoll专用的文件描述符; epoll\_event:用于回传代处理事件的数组; maxevents:每次能处理的事件数; timeout:等待I/O事件发生的超时值; 返回发生事件数。 相对于select模型中的select函数。 7. 例子程序 下面是一个简单Echo Server的例子程序,麻雀虽小,五脏俱全,还包含了一个简单的超时检查机制,简洁起见没有做错误处理。 **\[cpp\]** [ view plain][view plain] [copy][view plain] 1. // 2. // a simple echo server using epoll in linux 3. // 4. // 2009-11-05 5. // 2013-03-22:修改了几个问题,1是/n格式问题,2是去掉了原代码不小心加上的ET模式; 6. // 本来只是简单的示意程序,决定还是加上 recv/send时的buffer偏移 7. // by sparkling 8. // 9. \#include <sys/socket.h> 10. \#include <sys/epoll.h> 11. \#include <netinet/in.h> 12. \#include <arpa/inet.h> 13. \#include <fcntl.h> 14. \#include <unistd.h> 15. \#include <stdio.h> 16. \#include <errno.h> 17. \#include <iostream> 18. using namespace std; 19. \#define MAX\_EVENTS 500 20. struct myevent\_s 21. \{ 22. int fd; 23. void (\*call\_back)(int fd, int events, void \*arg); 24. int events; 25. void \*arg; 26. int status; // 1: in epoll wait list, 0 not in 27. char buff\[128\]; // recv data buffer 28. int len, s\_offset; 29. long last\_active; // last active time 30. \}; 31. // set event 32. void EventSet(myevent\_s \*ev, int fd, void (\*call\_back)(int, int, void\*), void \*arg) 33. \{ 34. ev->fd = fd; 35. ev->call\_back = call\_back; 36. ev->events = 0; 37. ev->arg = arg; 38. ev->status = 0; 39. bzero(ev->buff, sizeof(ev->buff)); 40. ev->s\_offset = 0; 41. ev->len = 0; 42. ev->last\_active = time(NULL); 43. \} 44. // add/mod an event to epoll 45. void EventAdd(int epollFd, int events, myevent\_s \*ev) 46. \{ 47. struct epoll\_event epv = \{0, \{0\}\}; 48. int op; 49. epv.data.ptr = ev; 50. epv.events = ev->events = events; 51. if(ev->status == 1)\{ 52. op = EPOLL\_CTL\_MOD; 53. \} 54. else\{ 55. op = EPOLL\_CTL\_ADD; 56. ev->status = 1; 57. \} 58. if(epoll\_ctl(epollFd, op, ev->fd, &epv) < 0) 59. printf("Event Add failed\[fd=%d\], evnets\[%d\]\\n", ev->fd, events); 60. else 61. printf("Event Add OK\[fd=%d\], op=%d, evnets\[%0X\]\\n", ev->fd, op, events); 62. \} 63. // delete an event from epoll 64. void EventDel(int epollFd, myevent\_s \*ev) 65. \{ 66. struct epoll\_event epv = \{0, \{0\}\}; 67. if(ev->status != 1) return; 68. epv.data.ptr = ev; 69. ev->status = 0; 70. epoll\_ctl(epollFd, EPOLL\_CTL\_DEL, ev->fd, &epv); 71. \} 72. int g\_epollFd; 73. myevent\_s g\_Events\[MAX\_EVENTS+1\]; // g\_Events\[MAX\_EVENTS\] is used by listen fd 74. void RecvData(int fd, int events, void \*arg); 75. void SendData(int fd, int events, void \*arg); 76. // accept new connections from clients 77. void AcceptConn(int fd, int events, void \*arg) 78. \{ 79. struct sockaddr\_in sin; 80. socklen\_t len = sizeof(struct sockaddr\_in); 81. int nfd, i; 82. // accept 83. if((nfd = accept(fd, (struct sockaddr\*)&sin, &len)) == -1) 84. \{ 85. if(errno != EAGAIN && errno != EINTR) 86. \{ 87. \} 88. printf("%s: accept, %d", \_\_func\_\_, errno); 89. return; 90. \} 91. do 92. \{ 93. for(i = 0; i < MAX\_EVENTS; i++) 94. \{ 95. if(g\_Events\[i\].status == 0) 96. \{ 97. break; 98. \} 99. \} 100. if(i == MAX\_EVENTS) 101. \{ 102. printf("%s:max connection limit\[%d\].", \_\_func\_\_, MAX\_EVENTS); 103. break; 104. \} 105. // set nonblocking 106. int iret = 0; 107. if((iret = fcntl(nfd, F\_SETFL, O\_NONBLOCK)) < 0) 108. \{ 109. printf("%s: fcntl nonblocking failed:%d", \_\_func\_\_, iret); 110. break; 111. \} 112. // add a read event for receive data 113. EventSet(&g\_Events\[i\], nfd, RecvData, &g\_Events\[i\]); 114. EventAdd(g\_epollFd, EPOLLIN, &g\_Events\[i\]); 115. \}while(0); 116. printf("new conn\[%s:%d\]\[time:%d\], pos\[%d\]\\n", inet\_ntoa(sin.sin\_addr), 117. ntohs(sin.sin\_port), g\_Events\[i\].last\_active, i); 118. \} 119. // receive data 120. void RecvData(int fd, int events, void \*arg) 121. \{ 122. struct myevent\_s \*ev = (struct myevent\_s\*)arg; 123. int len; 124. // receive data 125. len = recv(fd, ev->buff+ev->len, sizeof(ev->buff)-1-ev->len, 0); 126. EventDel(g\_epollFd, ev); 127. if(len > 0) 128. \{ 129. ev->len += len; 130. ev->buff\[len\] = '\\0'; 131. printf("C\[%d\]:%s\\n", fd, ev->buff); 132. // change to send event 133. EventSet(ev, fd, SendData, ev); 134. EventAdd(g\_epollFd, EPOLLOUT, ev); 135. \} 136. else if(len == 0) 137. \{ 138. close(ev->fd); 139. printf("\[fd=%d\] pos\[%d\], closed gracefully.\\n", fd, ev-g\_Events); 140. \} 141. else 142. \{ 143. close(ev->fd); 144. printf("recv\[fd=%d\] error\[%d\]:%s\\n", fd, errno, strerror(errno)); 145. \} 146. \} 147. // send data 148. void SendData(int fd, int events, void \*arg) 149. \{ 150. struct myevent\_s \*ev = (struct myevent\_s\*)arg; 151. int len; 152. // send data 153. len = send(fd, ev->buff + ev->s\_offset, ev->len - ev->s\_offset, 0); 154. if(len > 0) 155. \{ 156. printf("send\[fd=%d\], \[%d<->%d\]%s\\n", fd, len, ev->len, ev->buff); 157. ev->s\_offset += len; 158. if(ev->s\_offset == ev->len) 159. \{ 160. // change to receive event 161. EventDel(g\_epollFd, ev); 162. EventSet(ev, fd, RecvData, ev); 163. EventAdd(g\_epollFd, EPOLLIN, ev); 164. \} 165. \} 166. else 167. \{ 168. close(ev->fd); 169. EventDel(g\_epollFd, ev); 170. printf("send\[fd=%d\] error\[%d\]\\n", fd, errno); 171. \} 172. \} 173. void InitListenSocket(int epollFd, short port) 174. \{ 175. int listenFd = socket(AF\_INET, SOCK\_STREAM, 0); 176. fcntl(listenFd, F\_SETFL, O\_NONBLOCK); // set non-blocking 177. printf("server listen fd=%d\\n", listenFd); 178. EventSet(&g\_Events\[MAX\_EVENTS\], listenFd, AcceptConn, &g\_Events\[MAX\_EVENTS\]); 179. // add listen socket 180. EventAdd(epollFd, EPOLLIN, &g\_Events\[MAX\_EVENTS\]); 181. // bind & listen 182. sockaddr\_in sin; 183. bzero(&sin, sizeof(sin)); 184. sin.sin\_family = AF\_INET; 185. sin.sin\_addr.s\_addr = INADDR\_ANY; 186. sin.sin\_port = htons(port); 187. bind(listenFd, (const sockaddr\*)&sin, sizeof(sin)); 188. listen(listenFd, 5); 189. \} 190. int main(int argc, char \*\*argv) 191. \{ 192. unsigned short port = 12345; // default port 193. if(argc == 2)\{ 194. port = atoi(argv\[1\]); 195. \} 196. // create epoll 197. g\_epollFd = epoll\_create(MAX\_EVENTS); 198. if(g\_epollFd <= 0) printf("create epoll failed.%d\\n", g\_epollFd); 199. // create & bind listen socket, and add to epoll, set non-blocking 200. InitListenSocket(g\_epollFd, port); 201. // event loop 202. struct epoll\_event events\[MAX\_EVENTS\]; 203. printf("server running:port\[%d\]\\n", port); 204. int checkPos = 0; 205. while(1)\{ 206. // a simple timeout check here, every time 100, better to use a mini-heap, and add timer event 207. long now = time(NULL); 208. for(int i = 0; i < 100; i++, checkPos++) // doesn't check listen fd 209. \{ 210. if(checkPos == MAX\_EVENTS) checkPos = 0; // recycle 211. if(g\_Events\[checkPos\].status != 1) continue; 212. long duration = now - g\_Events\[checkPos\].last\_active; 213. if(duration >= 60) // 60s timeout 214. \{ 215. close(g\_Events\[checkPos\].fd); 216. printf("\[fd=%d\] timeout\[%d--%d\].\\n", g\_Events\[checkPos\].fd, g\_Events\[checkPos\].last\_active, now); 217. EventDel(g\_epollFd, &g\_Events\[checkPos\]); 218. \} 219. \} 220. // wait for events to happen 221. int fds = epoll\_wait(g\_epollFd, events, MAX\_EVENTS, 1000); 222. if(fds < 0)\{ 223. printf("epoll\_wait error, exit\\n"); 224. break; 225. \} 226. for(int i = 0; i < fds; i++)\{ 227. myevent\_s \*ev = (struct myevent\_s\*)events\[i\].data.ptr; 228. if((events\[i\].events&EPOLLIN)&&(ev->events&EPOLLIN)) // read event 229. \{ 230. ev->call\_back(ev->fd, events\[i\].events, ev->arg); 231. \} 232. if((events\[i\].events&EPOLLOUT)&&(ev->events&EPOLLOUT)) // write event 233. \{ 234. ev->call\_back(ev->fd, events\[i\].events, ev->arg); 235. \} 236. \} 237. \} 238. // free resource 239. return 0; 240. \} 转载于:http://blog.csdn.net/sparkliang/article/details/4770655 [view plain]: http://blog.csdn.net/sparkliang/article/details/4770655#
还没有评论,来说两句吧...