Linux下IO多路轉接技術之epoll

2021-07-15 21:37:41 字數 4063 閱讀 6793

在學習linux套接字程式設計的高階階段,寫下對epoll的一些自己的見解,當然,大部分是來自網路智慧型。

一。什麼是epoll

按照man⼿冊的說法:是為處理⼤批量控制代碼⽽作了改進的poll。當然,這不是2.6核心才有的,它是在2.5.44核心中被引進的(epoll(4) is a new api introduced in linux kernel

2.5.44),它⼏乎具備了之前所說的⼀切優點,被公認為linux2.6下效能最好的多路i/o就緒通知⽅法。

在了解epoll之前,我們要了解兩個函式:

select(2) 和 poll(2) 函式的功能:

epoll 沒有這些固定限制,也不執行任何線性掃瞄。因此它可以更高效地執行和處理大量事件。

二。epoll的函式及其功能:

1. int epoll_create(int size);

建立⼀個epoll的控制代碼。⾃從linux2.6.8之後,size引數是被忽略的。需要注意的是,當建立好epoll控制代碼後,它就是會佔⽤⼀個fd值,在linux下如果檢視/proc/程序id/fd/,是能夠看到這個fd的,所以在使⽤完epoll後,必須調⽤close()關閉,否則可能導致fd被耗盡。

2. int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

epoll的事件註冊函式,它不同於select()是在監聽事件時告訴核心要監聽什麼型別的事件,⽽是在這⾥先註冊要監聽的事件型別。

第⼀個引數是epoll_create()的返回值。

第⼆個參數列⽰動作,⽤三個巨集來表⽰:

epoll_ctl_add:註冊新的fd到epfd中;

epoll_ctl_mod:修改已經註冊的fd的監聽事件;

epoll_ctl_del:從epfd中刪除⼀個fd;

第三個引數是需要監聽的fd。

第四個引數是告訴核心需要監聽什麼事。struct epoll_event結構體如下:

typedef union epoll_data

epoll_data_t;

struct epoll_event

;

events可以是以下⼏個巨集的集合:

epollin :表⽰對應的⽂件描述符可以讀(包括對端socket正常關閉);

epollout:表⽰對應的⽂件描述符可以寫;

epollpri:表⽰對應的⽂件描述符有緊急的資料可讀(這⾥應該表⽰有帶外資料到來);

epollerr:表⽰對應的⽂件描述符發⽣錯誤;

epollhup:表⽰對應的⽂件描述符被結束通話;

epollet: 將epoll設為邊緣觸發(edge triggered)模式,這是相對於⽔平觸發(leveltriggered)來說的。

epolloneshot:只監聽⼀次事件,當監聽完這次事件之後,如果還需要繼續監聽這個socket的話,需要再次把這個socket加⼊到epoll佇列⾥。

3.int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);等待在epoll監控的事件中已經發生的事件。引數events是分配好的epoll_event結構體陣列,epoll將會把發⽣的事件賦值到events陣列中(events不可以是空指標,核心只負責把資料複製到這個events陣列中,不會去幫助我們在⽤戶態中分配記憶體)。maxevents告之核心這個events有多⼤,這個 maxevents的值不能⼤於建立epoll_create()時的size,引數timeout是超時時間(毫秒,0會⽴即返回,-1將不確定,也有說法說是永久阻塞)。如果函式調⽤成功,返回對應i/o上已準備好的⽂件描述符數⽬,如返回0表⽰已超時。

epoll的工作方式:

lt:水平觸發

,效率會低於et觸發,尤其在大併發,大流量的情況下。但是lt對**編寫要求比較低,不容易出現問題。lt模式服務編寫上的表現是:只要有資料沒有被獲取,核心就不斷通知你,因此不用擔心事件丟失的情況。

et:邊緣觸發

,效率非常高,在併發,大流量的情況下,會比lt少很多epoll的系統呼叫,因此效率高。但是對程式設計要求高,需要細緻的處理每個請求,否則容易發生丟失事件的情況。

下面舉乙個列子來說明lt和et的區別(都是非阻塞模式,阻塞就不說了,效率太低):

採用lt模式下, 如果accept呼叫有返回就可以馬上建立當前這個連線了,再epoll_wait等待下次通知,和select一樣。

但是對於et而言,如果accpet呼叫有返回,除了建立當前這個連線外,不能馬上就epoll_wait還需要繼續迴圈。

三。基於epoll的網路伺服器例項**:

現在開始寫**。我們要實現的是乙個可以接受到客戶端連線的並回顯一條訊息的伺服器。

首先我們要建立套接字並繫結:

static int startup(const char* _ip,int _port)

struct sockaddr_in local;

local.sin_family=af_inet;

local.sin_port=htons(_port);//host to net

local.sin_addr.s_addr=inet_addr(_ip);

if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0)

if(listen(sock,5)<0)

return sock;

}

現在我們已經得到了乙個監聽套接字,我們接下來需要做的是將這個套接字託管給epoll。

int main(int argc,const char* argv)

int listen_sock=startup(argv[1],atoi(argv[2]));
int epfd=epoll_create(256);

if(epfd<0)

struct epoll_event _ev;

_ev.events=epollin;

_ev.data.fd=listen_sock;

epoll_ctl(epfd,epoll_ctl_add,listen_sock,&_ev);

struct epoll_event _ready_ev[128];//create ready queue

int _ready_evs=128;

int _timeout=-1;//block

int nums=0;//num of fd_ready

建立好epoll並且將監聽套接字新增到epoll的事件集中,我們就只需要等待客戶端的鏈結請求,也就是某個事件發生。會將事件的檔案描述符給新增到就緒佇列中。每次,epoll只需要遍歷一遍就緒佇列就可以知道到底是有多少個事件已經就緒,而不像select一樣需要遍歷整個描述符集。

以下是epoll的主邏輯:

while(1)

}else

else if(_s==0)

else

}else if(_ready_ev[i].events & epollout)}}

}break;

} }

static int set_noblock(int sock)

至此,乙個伺服器就寫好了。我們可以開啟自己的瀏覽器輸入127.0.0.1:8080通過本機環回來測試。

測試結果如圖:

Linux下I O多路轉接之epoll

linux下i o多路轉接之epoll epoll i o event notification facility 在linux的網路程式設計中,很長的時間都在使用select來做事件觸發。在linux新的核心中,有了一種替換它的機制,就是epoll。相比於select,epoll最大的好處在於它不...

Linux下I O多路轉接之epoll 絕對經典

epoll 關於linux下i o多路轉接之epoll函式,什麼返回值,什麼引數,我不想再多的解釋,您不想移駕,我給你移來 返回值,引數說明等 最後將乙個用epoll設計的網路伺服器貼上 以供借閱 乙個流可以是檔案,socket,pipe等等可以進行i o操作的核心物件。不管是檔案,還是套接字,還是...

I O多路轉接

對於多個非阻塞i o,怎麼知道i o何時已經處於可讀或可寫狀態?如果採用迴圈一直呼叫write read,直到返回成功,這樣的方式成為輪詢 polling 大多數時間i o沒有處於就緒狀態,因此這樣的輪詢十分浪費cpu。一種比較好的技術是使用i o多路轉接,也叫做i o多路復用。其基本思想為 先構造...