Epoll 模型實現

2021-07-14 06:52:50 字數 4158 閱讀 1148

while

(true)   

else

if(m_events[i].data.fd == m_listen_sock_fd)

//如果新監測到乙個socket使用者連線到了繫結的socket埠,建立新的連線。 

else

if(m_events[i].events & epollin)

//如果是已經連線的使用者,並且收到資料,那麼進行讀入。 

onwriteepoll (i);//檢視當前的活動連線是否有需要寫出的資料。 

}   

catch

(int

)   

}   

m_bontimechecking = true;   

ontimer ();//進行一些定時的操作,主要就是刪除一些斷線使用者等。 

}   

其實epoll的精華,按照我目前的理解,也就是上述的幾段短短的**,看來時代真的不同了,以前如何接受大量使用者連線的問題,現在卻被如此輕鬆的搞定,真是讓人不得不感嘆。 

今天搞了一天的epoll,想做乙個高併發的**程式。剛開始真是鬱悶,一直搞不通,網上也有幾篇介紹epoll的文章。但都不深入,沒有將一些注意的地方講明。以至於走了很多彎路,現將自己的一些理解共享給大家,以少走彎路。 

epoll用到的所有函式都是在標頭檔案sys/epoll.h中宣告,有什麼地方不明白或函式忘記了可以去看一下。 

epoll和select相比,最大不同在於: 

1epoll返回時已經明確的知道哪個sokcet fd發生了事件,不用再乙個個比對。這樣就提高了效率。 

2select的fd_setsize是有限止的,而epoll是沒有限止的只與系統資源有關。 

1、epoll_create函式 

函式宣告:int epoll_create(int size) 

22、epoll_ctl函式 

函式宣告:int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) 

該函式用於控制某個epoll檔案描述符上的事件,可以註冊事件,修改事件,刪除事件。 

引數: 

epfd:由 epoll_create 生成的epoll專用的檔案描述符; 

op:要進行的操作例如註冊事件,可能的取值epoll_ctl_add 註冊、epoll_ctl_mod 修 改、epoll_ctl_del 刪除 

fd:關聯的檔案描述符; 

event:指向epoll_event的指標; 

如果呼叫成功返回0,不成功返回-1 

用到的資料結構 

[cpp]

view plain

copy

typedef

union

epoll_data  epoll_data_t;   

struct

epoll_event ;  

如: [cpp]

view plain

copy

struct

epoll_event ev;  

//設定與要處理的事件相關的檔案描述符 

[cpp]

view plain

copy

ev.data.fd=listenfd;   

//設定要處理的事件型別 

[cpp]

view plain

copy

ev.events=epollin|epollet;   

//註冊epoll事件 

[cpp]

view plain

copy

epoll_ctl(epfd,epoll_ctl_add,listenfd,&ev);   

常用的事件型別: 

epollin :表示對應的檔案描述符可以讀; 

epollout:表示對應的檔案描述符可以寫; 

epollpri:表示對應的檔案描述符有緊急的資料可讀 

epollerr:表示對應的檔案描述符發生錯誤; 

epollhup:表示對應的檔案描述符被結束通話; 

epollet:表示對應的檔案描述符有事件發生; 

3、epoll_wait函式 

函式宣告:int epoll_wait(int epfd,struct epoll_event * events,int maxevents,int timeout) 

該函式用於輪詢i/o事件的發生; 

引數: 

epfd:由epoll_create 生成的epoll專用的檔案描述符; 

epoll_event:用於回傳代處理事件的陣列; 

maxevents:每次能處理的事件數; 

timeout:等待i/o事件發生的超時值(單位我也不太清楚);-1相當於阻塞,0相當於非阻塞。一般用-1即可 

返回發生事件數。 

用法如下: 

[cpp]

view plain

copy

/*build the epoll enent for recall */

struct

epoll_event ev_read[20];   

intnfds = 0; 

//return the events count 

nfds=epoll_wait(epoll_fd,ev_read,20, -1);   

for(i=0; i   

setnonblocking(client);   

ev.events = epollin | epollet;   

ev.data.fd = client;   

if(epoll_ctl(kdpfd, epoll_ctl_add, client, &ev) < 0)    

}   

else

do_use_fd(events[n].data.fd);   

}   

}   

此時使用的是et模式,即,邊沿觸發,類似於電平觸發,epoll中的邊沿觸發的意思是只對新到的資料進行通知,而核心緩衝區中如果是舊資料則不進行通知,所以在do_use_fd函式中應該使用如下迴圈,才能將核心緩衝區中的資料讀完。       

[cpp]

view plain

copy

while

(1)    

dosomething with the recved data........   

}   

在上面例子中沒有說明對於listen socket fd該如何處理,有的時候會使用兩個執行緒,乙個用來監聽accept另乙個用來監聽epoll_wait,如果是這樣使用的話,則listen socket fd使用預設的阻塞方式就行了,而如果epoll_wait和accept處於乙個執行緒中,即,全部由epoll_wait進行監聽,則,需將listen socket fd也設定成非阻塞的,這樣,對accept也應該使用while包起來(類似於上面的recv),因為,epoll_wait返回時只是說有連線到來了,並沒有說有幾個連線,而且在et模式下epoll_wait不會再因為上一次的連線還沒讀完而返回,這種情況確實存在,我因為這個問題而耗費了一天多的時間,這裡需要說明的是,每呼叫一次accept將從核心中的已連線佇列中的隊頭讀取乙個連線,因為在併發訪問的環境下,有可能有多個連線「同時」到達,而epoll_wait只返回了一次。

唯一有點麻煩是epoll有2種工作方式:lt和et。 

lt(level triggered)是預設的工作方式,並且同時支援block和no-block socket.在這種做法中,核心告訴你乙個檔案描述符是否就緒了,然後你可以對這個就緒的fd進行io操作。如果你不作任何操作,核心還是會繼續通知你的,所以,這種模式程式設計出錯誤可能性要小一點。傳統的select/poll都是這種模型的代表. 

et (edge-triggered)是高速工作方式,只支援no-block socket。在這種模式下,當描述符從未就緒變為就緒時,核心通過epoll告訴你。然後它會假設你知道檔案描述符已經就緒,並且不會再為那個檔案描述符傳送更多的就緒通知,直到你做了某些操作導致那個檔案描述符不再為就緒狀態了(比如,你在傳送,接收或者接收請求,或者傳送接收的資料少於一定量時導致了乙個ewouldblock 錯誤)。但是請注意,如果一直不對這個fd作io操作(從而導致它再次變成未就緒),核心不會傳送更多的通知(only once),不過在tcp協議中,et模式的加速效用仍需要更多的benchmark確認。

epoll反應堆模型實現

在高併發tcp請求中,為了實現資源的節省,效率的提公升,epoll逐漸替代了之前的select和poll,它在使用者層上規避了忙輪詢這種效率不高的監聽方式,epoll的時間複雜度為o 1 也就意味著,epoll在高併發場景,隨著檔案描述符的增長,有良好的可擴充套件性。關鍵函式有三個 核心資料結構 t...

epoll模型例項

epoll有兩種模式,edge triggered 簡稱et 和 level triggered 簡稱lt 在採用這兩種模式時要注意的是,如果採用et模式,那麼僅當狀態發生變化時才會通知,而採用lt模式類似於原來的 select poll操作,只要還有沒有處理的事件就會一直通知.以 來說明問題 首先...

Epoll模型講解

首先我們來定義流的概念,乙個流可以是檔案,socket,pipe等等可以進行i o操作的核心物件。不管是檔案,還是套接字,還是管道,我們都可以把他們看作流。之後我們來討論i o的操作,通過read,我們可以從流中讀入資料 通過write,我們可以往流寫入資料。現在假定乙個情形,我們需要從流中讀資料,...