epoll的使用实例
在網(wǎng)絡(luò)編程中通常需要處理很多個(gè)連接,可以用select和poll來處理多個(gè)連接。但是select都受進(jìn)程能打開的最大文件描述符個(gè)數(shù)的限制。并且select和poll效率會(huì)隨著監(jiān)聽fd的數(shù)目增多而下降。
解決方法就是用epoll
1.epoll
是Linux內(nèi)核為處理大批量文件描述符而做了改進(jìn)的poll,是Linux下多路復(fù)用IO接口select/poll的增強(qiáng)版本。
2.epoll、select、poll的區(qū)別:
1)相比于select和poll,epoll最大的好處在于不會(huì)隨著監(jiān)聽fd數(shù)目的增加而降低效率
2)內(nèi)核中的select與poll的實(shí)現(xiàn)是采用輪詢來處理的,輪詢數(shù)目越多耗時(shí)就越多
3)epoll的實(shí)現(xiàn)是基于回調(diào)的,如果fd有期望的事件發(fā)生就會(huì)通過回調(diào)函數(shù)將其加入epoll就緒隊(duì)列中。也就是說它只關(guān)心“活躍”的fd,與fd數(shù)目無關(guān)。
4)內(nèi)核空間用戶空間數(shù)據(jù)拷貝問題,如何讓內(nèi)核把fd消息通知給用戶空間?select和poll采取了內(nèi)存拷貝的方法,而epoll采用的是共享內(nèi)存的方式。速度快
5)epoll不僅會(huì)告訴應(yīng)用程序有I/o事件的到來,還會(huì)告訴應(yīng)用程序相關(guān)的信息,這寫信息是應(yīng)用程序填充的,因此根據(jù)這寫信息應(yīng)用程序就能直接定位到事件,而不必遍歷整個(gè)fd集合。
6)poll和select受進(jìn)程能打開的最大文件描述符的限制。select還受FD_SETSIZE的限制。但是epoll的限制的最大可以打開文件的數(shù)目(cat /proc/sys/fs/file-max進(jìn)行查看)。
3.epoll的工作模式
有下面兩種工作模式,默認(rèn)是水平觸發(fā)。
EPOLLLT:水平觸發(fā)(Level Triggered),事件沒有處理完也會(huì)觸發(fā)。完全靠kernel epoll驅(qū)動(dòng),應(yīng)用程序只需要處理從epoll_wait返回的fds。這些fds我們認(rèn)為他們處于就緒狀態(tài)。
LT模式支持阻塞fd和非阻塞fd。
EPOLLET:邊沿觸發(fā)(Edge Triggered)。只有空閑->就緒才會(huì)觸發(fā)。應(yīng)用程序需要維護(hù)一個(gè)就緒隊(duì)列。
此模式下,系統(tǒng)僅僅通知應(yīng)用程序哪些fds變成了就緒狀態(tài),一旦fd變成了就緒狀態(tài),epoll將不再關(guān)注這個(gè)fd的任何狀態(tài)信息(從epoll隊(duì)列移除)。
直到應(yīng)用程序通過讀寫操作觸發(fā)EAGAIN狀態(tài),epoll認(rèn)為這個(gè)fd又變成空閑狀態(tài),那么epoll又重新關(guān)注這個(gè)fd的狀態(tài)變化(重新加入epoll隊(duì)列中)。
隨著epoll_wait的返回,隊(duì)列中的fds是減少的,所以在大并發(fā)的系統(tǒng)中,EPOLLET更有優(yōu)勢(shì)。但是對(duì)程序員的要求也更高。
ET模式只支持non-block socket,以避免由于一個(gè)文件句柄的阻塞讀/阻塞寫把處理多個(gè)文件描述符的任務(wù)餓死。
4.如何使用
主要是下面幾個(gè)函數(shù)和結(jié)構(gòu)體。
?#include <sys/epoll.h>
?????? int epoll_create(int size);
?????? int epoll_create1(int flags);
?????? int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
?????? int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);
?????? The struct epoll_event is defined as :
?????????? typedef union epoll_data {
?????????????? void????*ptr;
?????????????? int??????fd;
?????????????? uint32_t u32;
?????????????? uint64_t u64;
?????????? } epoll_data_t;
?????????? struct epoll_event {
?????????????? uint32_t???? events;????/* Epoll events */
?????????????? epoll_data_t data;??????/* User data variable */
?????????? };
1)int epoll_create(int size);
創(chuàng)建一個(gè)epoll的句柄,
size:告訴內(nèi)核這個(gè)句柄可以監(jiān)聽的數(shù)目一共多大。
注意這里返回一個(gè)句柄,也是一個(gè)文件描述符,后面還是要關(guān)閉的。
2)int epoll_create1(int flags);
上面那個(gè)的加強(qiáng)版,flags可以是EPOLL_CLOEXEC,表示具有執(zhí)行后關(guān)閉的特性
??????????????Set the close-on-exec (FD_CLOEXEC) flag on the new file descrip‐
??????????????tor.??See the description of the O_CLOEXEC flag in??open(2)??for
??????????????reasons why this may be useful
?
3)?int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
注冊(cè)函數(shù),用來對(duì)創(chuàng)建的epollfd進(jìn)行操作的。
epfd:create出來的fd
op:執(zhí)行的動(dòng)作,由3個(gè)宏來表示
EPOLL_CTL_ADD:注冊(cè)新的fd到epfd中;
EPOLL_CTL_MOD:修改已經(jīng)注冊(cè)的fd的監(jiān)聽事件;
EPOLL_CTL_DEL:從epfd中刪除一個(gè)fd;
fd:要監(jiān)聽的fd
event:表示需要監(jiān)聽的事件
結(jié)構(gòu)見上面。
events可以是以下幾個(gè)宏的集合:
EPOLLIN :表示對(duì)應(yīng)的文件描述符可以讀(包括對(duì)端SOCKET正常關(guān)閉);
EPOLLOUT:表示對(duì)應(yīng)的文件描述符可以寫;
EPOLLPRI:表示對(duì)應(yīng)的文件描述符有緊急的數(shù)據(jù)可讀(這里應(yīng)該表示有帶外數(shù)據(jù)到來);
EPOLLERR:表示對(duì)應(yīng)的文件描述符發(fā)生錯(cuò)誤;
EPOLLHUP:表示對(duì)應(yīng)的文件描述符被掛斷;
EPOLLET: 將EPOLL設(shè)為邊緣觸發(fā)(Edge Triggered)模式,這是相對(duì)于水平觸發(fā)(Level Triggered)來說的。
EPOLLONESHOT:只監(jiān)聽一次事件,當(dāng)監(jiān)聽完這次事件之后,如果還需要繼續(xù)監(jiān)聽這個(gè)socket的話,需要再次把這個(gè)socket加入到EPOLL隊(duì)列里
4)??int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);
用來等待事件的產(chǎn)生
epfd:create出來的epollfd
events:從內(nèi)核得到事件的集合。一般的一個(gè)數(shù)組
maxevents:用來告訴內(nèi)核events有多大,不能大于epoll_create()時(shí)的size。
timeout是超時(shí)時(shí)間:單位的毫秒,為0表示立即返回,-1表示阻塞。
返回:0表示超時(shí),>0表示需要處理的事件數(shù)目。<0表示出錯(cuò)
5.實(shí)例:
server端是一個(gè)回射服務(wù)器:
client:連接服務(wù)器,通過終端發(fā)送數(shù)據(jù)給server,并接收server發(fā)來的數(shù)據(jù)。
#include<sys/types.h> #include<sys/socket.h> #include<netinet/in.h> #include<arpa/inet.h> #include<sys/select.h> #include<stdlib.h> #include<stdio.h> #include<string.h> void select_test(int conn) {int ret = 0;fd_set rset;FD_ZERO(&rset);int nready;int maxfd = conn;int fd_stdin = fileno(stdin);if(fd_stdin > maxfd){maxfd = fd_stdin;}int len = 0;char readbuf[1024] = {0};char writebuf[1024] = {0};while(1){FD_ZERO(&rset);FD_SET(fd_stdin, &rset);FD_SET(conn, &rset);nready = select(maxfd+1, &rset, NULL, NULL, NULL);if(nready == -1){perror("select");exit(0);}else if(nready == 0){continue; }if(FD_ISSET(conn, &rset)){ret = read(conn, readbuf, sizeof(readbuf));if(ret == 0){printf("server close1\n");break;}else if(-1 == ret){perror("read1");break;} fputs(readbuf, stdout);memset(readbuf, 0, sizeof(readbuf));} if(FD_ISSET(fd_stdin, &rset)){ read(fd_stdin, writebuf, sizeof(writebuf)); len = strlen(writebuf);ret = write(conn, writebuf, len);if(ret == 0){printf("server close3\n");break;}else if(-1 == ret){perror("write");break;}memset(writebuf, 0, sizeof(writebuf)); }} } int sockfd = 0; int main(int argc, char **argv) {sockfd = socket(AF_INET, SOCK_STREAM, 0);if(sockfd < 0){perror("socket");return -1;}unsigned short sport = 8080;if(argc == 2){sport = atoi(argv[1]);}struct sockaddr_in addr;addr.sin_family = AF_INET;printf("port = %d\n", sport);addr.sin_port = htons(sport);addr.sin_addr.s_addr = inet_addr("127.0.0.1");if(connect(sockfd, (struct sockaddr*)&addr, sizeof(addr)) < 0){perror("connect");return -2;}struct sockaddr_in addr2;socklen_t len = sizeof(addr2);if(getpeername(sockfd, (struct sockaddr*)&addr2, &len) < 0){perror("getsockname");return -3;}printf("Server: port:%d, ip:%s\n", ntohs(addr2.sin_port), inet_ntoa(addr2.sin_addr));select_test(sockfd);close(sockfd);return 0; }還可以進(jìn)行暴力連接測(cè)試。可以參考:?http://www.cnblogs.com/xcywt/p/8120166.html 這個(gè)例子的客戶端就是暴力連接測(cè)試的。
測(cè)試結(jié)果可以和select實(shí)現(xiàn)的server進(jìn)行比較,發(fā)現(xiàn)在虛擬機(jī)上epollserver好像沒有快多少(我也暫時(shí)沒找到原因)。
但是在服務(wù)器上,還是有很快的,效率提升了很多。
總結(jié)
以上是生活随笔為你收集整理的epoll的使用实例的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 关于mac注册机core keygen在
- 下一篇: etcd集群部署与遇到的坑