epoll工作模式詳解_第1頁
epoll工作模式詳解_第2頁
epoll工作模式詳解_第3頁
epoll工作模式詳解_第4頁
epoll工作模式詳解_第5頁
已閱讀5頁,還剩2頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

我們目前的網(wǎng)絡(luò)模型大都是epoll的,因?yàn)閑poll模型會比select模型性能高很多,尤其在大連接數(shù)的情況下,作為后臺開發(fā)人員需要理解其中的原因。select/epoll的特點(diǎn)select的特點(diǎn):select選擇句柄的時候,是遍歷所有句柄,也就是說句柄有事件響應(yīng)時,select需要遍歷所有句柄才能獲取到哪些句柄有事件通知,因此效率是非常低。但是如果連接很少的情況下,select和epoll的LT觸發(fā)模式相比,性能上差別不大。這里要多說一句,select支持的句柄數(shù)是有限制的,同時只支持1024個,這個是句柄集合限制的,如果超過這個限制,很可能導(dǎo)致溢出,而且非常不容易發(fā)現(xiàn)問題,TAF就出現(xiàn)過這個問題,調(diào)試了n天,才發(fā)現(xiàn):)當(dāng)然可以通過修改linux的socket內(nèi)核調(diào)整這個參數(shù)。epoll的特點(diǎn):epoll對于句柄事件的選擇不是遍歷的,是事件響應(yīng)的,就是句柄上事件來就馬上選擇出來,不需要遍歷整個句柄鏈表,因此效率非常高,內(nèi)核將句柄用紅黑樹保存的。對于epoll而言還有ET和LT的區(qū)別,LT表示水平觸發(fā),ET表示邊緣觸發(fā),兩者在性能以及代碼實(shí)現(xiàn)上差別也是非常大的。epoll的LT和ET的區(qū)別LT:水平觸發(fā),效率會低于ET觸發(fā),尤其在大并發(fā),大流量的情況下。但是LT對代碼編寫要求比較低,不容易出現(xiàn)問題。LT模式服務(wù)編寫上的表現(xiàn)是:只要有數(shù)據(jù)沒有被獲取,內(nèi)核就不斷通知你,因此不用擔(dān)心事件丟失的情況。ET:邊緣觸發(fā),效率非常高,在并發(fā),大流量的情況下,會比LT少很多epoll的系統(tǒng)調(diào)用,因此效率高。但是對編程要求高,需要細(xì)致的處理每個請求,否則容易發(fā)生丟失事件的情況。下面舉一個列子來說明LT和ET的區(qū)別(都是非阻塞模式,阻塞就不說了,效率太低):采用LT模式下,如果accept調(diào)用有返回就可以馬上建立當(dāng)前這個連接了,再epoll_wait等待下次通知,和select一樣。但是對于ET而言,如果accpet調(diào)用有返回,除了建立當(dāng)前這個連接外,不能馬上就epoll_wait還需要繼續(xù)循環(huán)accpet,直到返回-1,且errno==EAGAIN,TAF里面的示例代碼:if(ev.events&EPOLLIN){do{structsockaddr_instSockAddr;socklen_tiSockAddrSize=sizeof(sockaddr_in);TC_Socketcs;cs.setOwner(false);//接收連接TC_Sockets;s.init(fd,false,AF_INET);intiRetCode=s.accept(cs,(structsockaddr*)&stSockAddr,iSockAddrSize);if(iRetCode>0){…建立連接}else{//直到發(fā)生EAGAIN才不繼續(xù)acceptif(errno==EAGAIN){break;}}}while(true);}同樣,recv/send等函數(shù),都需要到errno==EAGAIN從本質(zhì)上講:與LT相比,ET模型是通過減少系統(tǒng)調(diào)用來達(dá)到提高并行效率的。epollET詳解ET模型的邏輯:內(nèi)核的讀buffer有內(nèi)核態(tài)主動變化時,內(nèi)核會通知你,無需再去mod。寫事件是給用戶使用的,最開始add之后,內(nèi)核都不會通知你了,你可以強(qiáng)制寫數(shù)據(jù)(直到EAGAIN或者實(shí)際字節(jié)數(shù)小于需要寫的字節(jié)數(shù)),當(dāng)然你可以主動modOUT,此時如果句柄可以寫了(sendbuffer有空間),內(nèi)核就通知你。這里內(nèi)核態(tài)主動的意思是:內(nèi)核從網(wǎng)絡(luò)接收了數(shù)據(jù)放入了讀buffer(會通知用戶IN事件,即用戶可以recv數(shù)據(jù))并且這種通知只會通知一次,如果這次處理(recv)沒有到剛才說的兩種情況(EAGIN或者實(shí)際字節(jié)數(shù)小于需要讀寫的字節(jié)數(shù)),則該事件會被丟棄,直到下次buffer發(fā)生變化。與LT的差別就在這里體現(xiàn),LT在這種情況下,事件不會丟棄,而是只要讀buffer里面有數(shù)據(jù)可以讓用戶讀,則不斷的通知你。另外對于ET而言,當(dāng)然也不一定非send/recv到前面所述的結(jié)束條件才結(jié)束,用戶可以自己隨時控制,即用戶可以在自己認(rèn)為合適的時候去設(shè)置IN和OUT事件:1如果用戶主動epoll_modOUT事件,此時只要該句柄可以發(fā)送數(shù)據(jù)(發(fā)送buffer不滿),則epoll_wait就會響應(yīng)(有時候采用該機(jī)制通知epoll_wai醒過來)。2如果用戶主動epoll_modIN事件,只要該句柄還有數(shù)據(jù)可以讀,則epoll_wait會響應(yīng)。這種邏輯在普通的服務(wù)里面都不需要,可能在某些特殊的情況需要。但是請注意,如果每次調(diào)用的時候都去epollmod將顯著降低效率,已經(jīng)吃過幾次虧了!因此采用et寫服務(wù)框架的時候,最簡單的處理就是:建立連接的時候epoll_addIN和OUT事件,后面就不需要管了每次read/write的時候,到兩種情況下結(jié)束:發(fā)生EAGAINread/write的實(shí)際字節(jié)數(shù)小于需要讀寫的字節(jié)數(shù)對于第二點(diǎn)需要注意兩點(diǎn):A:如果是UDP服務(wù),處理就不完全是這樣,必須要recv到發(fā)生EAGAIN為止,否則就丟失事件了。因?yàn)閁DP和TCP不同,是有邊界的,每次接收一定是一個完整的UDP包,當(dāng)然recv的buffer需要至少大于一個UDP包的大小。隨便再說一下,一個UDP包到底應(yīng)該多大?對于internet,由于MTU的限制,UDP包的大小不要超過576個字節(jié),否則容易被分包,對于公司的IDC環(huán)境,建議不要超過1472,否則也比較容易分包。B如果發(fā)送方發(fā)送完數(shù)據(jù)以后,就close連接,這個時候如果recv到數(shù)據(jù)是實(shí)際字節(jié)數(shù)小于讀寫字節(jié)數(shù),根據(jù)開始所述就認(rèn)為到EAGIN了從而直接返回,等待下一次事件,這樣是有問題的,close事件丟失了!因此如果依賴這種關(guān)閉邏輯的服務(wù),必須接收數(shù)據(jù)到EAGIN為止,例如lb。#include<iostream>#include<sys/socket.h>#include<sys/epoll.h>#include<netinet/in.h>#include<arpa/inet.h>#include<fcntl.h>#include<unistd.h>#include<stdio.h>#include<pthread.h>#defineMAXLINE10#defineOPEN_MAX100#defineLISTENQ20#defineSERV_PORT5555#defineINFTIM1000〃線程池任務(wù)隊(duì)列結(jié)構(gòu)體structtask(intfd; 〃需要讀寫的文件描述符structtask*next;//下一個任務(wù)};//用于讀寫兩個的兩個方面?zhèn)鬟f參數(shù)structuser_data(intfd;unsignedintn_size;charline[MAXLINE];};〃線程的任務(wù)函數(shù)void*readtask(void*args);void*writetask(void*args);〃聲明epoll_event結(jié)構(gòu)體的變量,ev用于注冊事件,數(shù)組用于回傳要處理的事件structepoll_eventev,events[20];intepfd;pthread_mutex_tmutex;pthread_cond_tcond1;structtask*readhead=NULL,*readtail=NULL,*writehead=NULL;voidsetnonblocking(intsock){intopts;opts=fcntl(sock,F_GETFL);if(opts<0){perror("fcntl(sock,GETFL)");exit(1);}opts=optslO_NONBLOCK;if(fcntl(sock,F_SETFL,opts)<0){perror("fcntl(sock,SETFL,opts)”);exit(1);intmain(){inti,maxi,listenfd,connfd,sockfd,nfds;pthread_ttid1,tid2;structtask*new_task=NULL;structuser_data*rdata=NULL;socklen_tclilen;pthread_mutex_init(&mutex,NULL);pthread_cond_init(&cond1,NULL);//初始化用于讀線程池的線程pthread_create(&tid1,NULL,readtask,NULL);pthread_create(&tid2,NULL,readtask,NULL);〃生成用于處理accept的epoll專用的文件描述符epfd=epoll_create(256);structsockaddr_inclientaddr;structsockaddr_inserveraddr;listenfd=socket(AF_INET,SOCK_STREAM,0);//把socket設(shè)置為非阻塞方式setnonblocking(listenfd);〃設(shè)置與要處理的事件相關(guān)的文件描述符ev.data.fd=listenfd;〃設(shè)置要處理的事件類型ev.events=EPOLLIN|EPOLLET;//注冊epoll事件epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev);bzero(&serveraddr,sizeof(serveraddr));serveraddr.sin_family=AF_INET;char*local_addr="200.200.200.222”;inet_aton(local_addr,&(serveraddr.sin_addr));//htons(SERV_PORT);serveraddr.sin_port=htons(SERV_PORT);bind(listenfd,(sockaddr*)&serveraddr,sizeof(serveraddr));listen(listenfd,LISTENQ);maxi=0;for(;;){//等待epoll事件的發(fā)生nfds=epoll_wait(epfd,events,20,500);//500超時時間〃處理所發(fā)生的所有事件for(i=0;i<nfds;++i){if(events[i].data.fd==listenfd){connfd=accept(listenfd,(sockaddr*)&clientaddr,&clilen);if(connfd<0){perror("connfd<0");exit(1);}setnonblocking(connfd);char*str=inet_ntoa(clientaddr.sin_addr);std::cout<<"connec_from>>"<<str<<std::endl;〃設(shè)置用于讀操作的文件描述符ev.data.fd=connfd;〃設(shè)置用于注測的讀操作事件ev.events=EPOLLIN|EPOLLET;//注冊evepoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev);}elseif(events[i].events&EPOLLIN){printf("reading!\n");if((sockfd=events[i].data.fd)<0)continue;new_task=newtask();new_task->fd=sockfd;new_task->next=NULL;//添加新的讀任務(wù)pthread_mutex_lock(&mutex);if(readhead==NULL){readhead=new_task;readtail=new_task;}else{readtail->next=new_task;readtail=new_task;}〃喚醒所有等待cond1條件的線程pthread_cond_broadcast(&cond1);pthread_mutex_unlock(&mutex);}elseif(events[i].events&EPOLLOUT){rdata=(structuser_data*)events[i].data.ptr;sockfd=rdata->fd;write(sockfd,rdata->line,rdata->n_size);deleterdata;//設(shè)置用于讀操作的文件描述符ev.data.fd=sockfd;〃設(shè)置用于注測的讀操作事件ev.events=EPOLLINIEPOLLET;〃修改sockfd上要處理的事件為EPOLINepoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);}}}}void*readtask(void*args){intfd=-1;unsignedintn;//用于把讀出來的數(shù)據(jù)傳遞出去structuser_data*data=NULL;while(1){pthread_mutex_lock(&mutex);//等待

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論