久久婷婷香蕉热狠狠综合,精品无码国产自产拍在线观看蜜,寡妇房东在做爰3,中文字幕日本人妻久久久免费,国产成人精品三上悠亚久久

當前位置:首頁 > 嵌入式培訓 > 嵌入式學習 > 講師博文 > EPOLL的(de)工作原理及流程

EPOLL的工(gong)作原理(li)及流程 時間:2018-05-14      來源:未知

一.Epoll是什么(me)?

epoll是個什么東(dong)東(dong)呢?按照man手冊的(de)說(shuo)法:是為(wei)處理大批(pi)量(liang)句柄而作了改進的(de)poll。當(dang)然,這(zhe)不是2.6內核才有的(de),它是在(zai)2.5.44內核中被引進的(de)(epoll(4) is a new API introduced in Linux kernel 2.5.44),它幾(ji)乎具備了之前所說(shuo)的(de)一切優(you)點,被公認為(wei)Linux2.6下性(xing)能最好的(de)多(duo)路I/O就緒(xu)通知方法。

二(er).epoll與poll和(he)select對比

[1]select 的缺(que)點:

單個進程能(neng)夠監(jian)視(shi)的文(wen)件(jian)(jian)描述(shu)符的數量(liang)(liang)存在最大限制(zhi),通常是1024,當然可以更改數量(liang)(liang),但由于select采用輪(lun)詢的方式掃(sao)描文(wen)件(jian)(jian)描述(shu)符,文(wen)件(jian)(jian)描述(shu)符數量(liang)(liang)越多(duo),性能(neng)越差;(在linux內核頭文(wen)件(jian)(jian)中,有這樣的定義(yi):#define __FD_SETSIZE 1024)

內(nei)核 / 用戶空間(jian)內(nei)存(cun)拷貝(bei)問題,select需要復制大量(liang)的句(ju)柄數據結(jie)構,產(chan)生巨大的開銷;

select返(fan)回的是(shi)含有整(zheng)(zheng)個句柄的數組,應用程序需(xu)要遍歷(li)整(zheng)(zheng)個數組才能發現哪些句柄發生了事件;

select中應用(yong)程(cheng)序如果沒有完成對一個(ge)已經就緒的(de)文件描述(shu)(shu)符進行IO操作(zuo),那么之(zhi)后每次select調(diao)用(yong)還是會將(jiang)這些文件描述(shu)(shu)符通知進程(cheng)。

相對于我們的(de)select模型,我們的(de)poll是使用鏈表保持文件描述符,因此(ci)沒(mei)有(you)了監視(shi)文件數量的(de)限(xian)制,但是2,3,4等缺點(dian)依舊(jiu)存在。

拿select模型(xing)(xing)為例(li),假設我(wo)們的服務(wu)(wu)器需(xu)要(yao)(yao)支(zhi)持100萬(wan)的并發連(lian)接(jie),則在__FD_SETSIZE 為1024的情況下,則我(wo)們至少需(xu)要(yao)(yao)開辟1k個進程才能實(shi)現100萬(wan)的并發連(lian)接(jie)。除(chu)了進程間(jian)上(shang)下文切換的時間(jian)消耗外(wai),從內核/用戶空間(jian)大量的無(wu)腦(nao)內存(cun)拷貝、數(shu)組(zu)輪詢等,是系統難(nan)以承受的。因此,基于(yu)select模型(xing)(xing)的服務(wu)(wu)器程序,要(yao)(yao)達到(dao)10萬(wan)級(ji)別的并發訪問,是一個很難(nan)完成的任務(wu)(wu)。

因此(ci),該epoll上場了。

三.Epoll的工作原理

設想一(yi)(yi)下(xia)如(ru)(ru)下(xia)場(chang)景:有(you)100萬個客戶端同時與一(yi)(yi)個服務器進程(cheng)保持著TCP連接。而每一(yi)(yi)時刻(ke),通常只有(you)幾百(bai)上千(qian)個TCP連接是活躍的(de)(事(shi)實上大部分場(chang)景都是這種情(qing)況)。如(ru)(ru)何實現這樣(yang)的(de)高并(bing)發(fa)?

在select/poll時(shi)代,服務器進程每次都把這(zhe)100萬個連(lian)接(jie)告訴操(cao)作系統(從(cong)用戶態復(fu)制句(ju)柄數據(ju)結構到內核(he)態),讓操(cao)作系統內核(he)去(qu)查詢這(zhe)些套接(jie)字上是否有事(shi)件發(fa)生,輪詢完后,再(zai)將句(ju)柄數據(ju)復(fu)制到用戶態,讓服務器應(ying)用程序輪詢處(chu)理已發(fa)生的(de)網絡事(shi)件,這(zhe)一過程資(zi)源消耗較大,因此,select/poll一般只能(neng)處(chu)理幾(ji)千(qian)的(de)并(bing)發(fa)連(lian)接(jie)。

epoll的設計和實現(xian)(xian)與select完全不同。epoll通過在Linux內核中申(shen)請一個(ge)簡易的文件(jian)系(xi)統(tong)(文件(jian)系(xi)統(tong)一般用什么數據結構實現(xian)(xian)?二叉樹樹)。然后epoll的調用分(fen)成了3個(ge)部分(fen):

1)調用epoll_create()建(jian)立一個(ge)epoll對(dui)象(xiang)(在epoll文件系統中為這個(ge)句柄對(dui)象(xiang)分配(pei)資(zi)源)

2)調用epoll_ctl向(xiang)epoll對象中添加這100萬個連接(jie)的套(tao)接(jie)字(zi)

3)調用epoll_wait收集發生的(de)事件的(de)連接

如此一(yi)(yi)來,要實現上面(mian)說是的(de)場景,只需要在進程啟動時(shi)建立一(yi)(yi)個(ge)(ge)epoll對(dui)象(xiang)(xiang),然后在需要的(de)時(shi)候向這(zhe)(zhe)個(ge)(ge)epoll對(dui)象(xiang)(xiang)中添加(jia)或者刪除(chu)連接。同時(shi),epoll_wait的(de)效率也非(fei)常(chang)高,因為調用epoll_wait時(shi),并(bing)沒有一(yi)(yi)股(gu)腦(nao)的(de)向操作系統復制(zhi)這(zhe)(zhe)100萬個(ge)(ge)連接的(de)句柄數據,內核也不需要去(qu)遍歷全部的(de)連接。

具體流程:

[1]當我們某(mou)個(ge)進程(cheng)調用epoll_create()函數的時候,linux內(nei)核會默認創建一(yi)個(ge)eventpoll結(jie)構(gou)體(ti),這個(ge)結(jie)構(gou)體(ti)中有兩個(ge)成員與epoll的使(shi)用方式相關。

每一個epoll對(dui)象都有一個獨立的(de)(de)eventpoll結構體,用于存放(fang)通過(guo)epoll_ctl方法向epoll對(dui)象中添加進來的(de)(de)事(shi)件。這些事(shi)件都會掛載(zai)在紅(hong)黑樹中,如此,重復添加的(de)(de)事(shi)件就可(ke)以通過(guo)紅(hong)黑樹而高效的(de)(de)識別出來.

 而所有添加(jia)到epoll中(zhong)的(de)事(shi)件都會(hui)(hui)與設備(網卡)驅動程(cheng)序建立回調關系,也就是(shi)說,當相應的(de)事(shi)件發生(sheng)時會(hui)(hui)調用這個回調方法。這個回調方法在(zai)內核中(zhong)叫(jiao)ep_poll_callback,它會(hui)(hui)將發生(sheng)的(de)事(shi)件epitem添加(jia)到rdlist雙鏈表中(zhong)。

在epoll中,對于每(mei)一個事(shi)件,都(dou)會建立一個epitem結(jie)構體,如下所示:

當調用epoll_wait檢查(cha)是否有事件(jian)發生時,只(zhi)需要檢查(cha)eventpoll對象中的(de)rdlist雙(shuang)鏈(lian)表中是否有epitem元素即可(ke)。如果rdlist不為空(kong),則把發生的(de)事件(jian)復制到用戶態,同時將事件(jian)數量返回給用戶。

總結:

(1)我(wo)們(men)我(wo)們(men)調用epoll_wait()函(han)數(shu)的時候,系統創建一(yi)個epoll對象(xiang),每個對象(xiang)都有一(yi)個

叫做eventpoll類型的結構體與之對應(ying),該結構體中主要(yao)有(you)兩(liang)個(ge)主要(yao)的成員(yuan),一個(ge)是

rbn,代表將要通(tong)過epoll_ctl向epll對象中添加(jia)的事(shi)件。這(zhe)些事(shi)情都(dou)是掛載在紅(hong)黑樹中。

一個是rdlist,里(li)面(mian)存放(fang)的是將要(yao)發生的事(shi)件

(2)當(dang)我們使用epoll_ctrl()函數(shu)的時(shi)候(hou),就(jiu)是向epoll對象中添加,刪除,修改感(gan)興趣(qu)的事件

(3) epoll_wait()系統。通過此(ci)調用收(shou)集在(zai)epoll監控(kong)中(zhong)已經(jing)發(fa)生的事(shi)件。當監控(kong)的事(shi)件狀態(tai)發(fa)生改變的時候,我們會調用會調用函數把epitem加(jia)入到rdlist中(zhong)去。

一. Epoll的(de)API函(han)數接口

3.1 事件(jian)的創建---epoll_create();

int epoll_create(int size);

int epoll_create1(int flags);

功(gong)能:poll_create()創建一(yi)個epoll的(de)事(shi)例,通知內(nei)核需要監聽size個fd。size指的(de)并不是(shi)(shi)最大的(de)后備存儲設備,而是(shi)(shi)衡量內(nei)核內(nei)部結構大小的(de)一(yi)個提示。當創建成功(gong)后,會占用(yong)一(yi)個fd,所以記得在使用(yong)完之(zhi)后調用(yong)close(),否則fd可能會被(bei)耗盡。

Note:自從Linux2.6.8版本以后,size值其(qi)實是沒什么(me)用的,不過(guo)要大(da)于0,因為內核(he)可以動態的分配大(da)小,所以不需(xu)要size這個提示了。

其次:epoll_create1()函數,其實它和epoll_create差不多,不同的是epoll_create1函參數flag:

· 當flag是0時,表示(shi)和epoll_create函(han)數完全一樣(yang),不需要size的(de)提示(shi)了;

· 當flag = EPOLL_CLOEXEC,創建的epfd會設置FD_CLOEXEC;

· 當(dang)flag = EPOLL_NONBLOCK,創建的epfd會設置為非阻塞。

一般用法都是使用EPOLL_CLOEXEC。

Note:關(guan)于(yu)FD_CLOEXEC,它是(shi)fd的(de)一個(ge)標識說明(ming),用來設置文件close-on-exec狀(zhuang)(zhuang)態(tai)的(de)。當close-on-exec狀(zhuang)(zhuang)態(tai)為0時,調用exec時,fd不會被(bei)關(guan)閉;狀(zhuang)(zhuang)態(tai)非零時則會被(bei)關(guan)閉,這樣做可(ke)以防(fang)止fd泄露給執行exec后的(de)進程。

返回值:成(cheng)功返回一個(ge)非(fei)負的文件(jian)描述符。

例如:

int epfd = epoll_create(20); //注:20為隨機寫的一個值,大于0即可。

int epfd = epoll_create1(0);

3.1 事(shi)件的注(zhu)冊---epoll_ctl();

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

功能:epoll的(de)(de)事件(jian)注冊函數,epoll的(de)(de)事件(jian)注冊函數,它(ta)不同于select()是在(zai)監聽事件(jian)時告訴內核要監聽什(shen)么類型的(de)(de)事件(jian),而是在(zai)這(zhe)里先注冊要監聽的(de)(de)事件(jian)類型。

參數:

@epfd epoll_create()函數的返回值(zhi)

@op 表(biao)示參(can)數的(de)動作(zuo),常用以下宏:

EPOLL_CTL_ADD:注冊新(xin)的fd到epfd中;

 EPOLL_CTL_MOD:修(xiu)改(gai)已(yi)經注(zhu)冊的(de)(de)fd的(de)(de)監聽事件(jian);

EPOLL_CTL_DEL:從(cong)epfd中(zhong)刪除一個(ge)fd;

@fd 表示我們需(xu)要監聽(ting)的文件描(miao)述符

@event 表(biao)示告訴內核(he),我們需要(yao)監(jian)聽什么事件。

結構體如下:

typedef union epoll_data

{

void *ptr;

int fd; //保存我(wo)們(men)使(shi)用的sockfd

uint32_t u32;

uint64_t u64;

} epoll_data_t;

struct epoll_event

{

uint32_t events; /* Epoll events */

epoll_data_t data; /* User data variable */

};

events參數是一個枚(mei)舉的集合,可(ke)以用” | “來增加事件類型,枚(mei)舉如下:

· EPOLLIN :表示對(dui)應的文件描(miao)述符可以(yi)讀(du)(包(bao)括對(dui)端SOCKET正常(chang)關閉);

· EPOLLOUT:表示對應的文件描述(shu)符可以寫;

· EPOLLPRI:表(biao)示(shi)對應的(de)文(wen)件描述符(fu)有(you)(you)緊急的(de)數(shu)據(ju)可讀(這(zhe)里應該表(biao)示(shi)有(you)(you)帶外數(shu)據(ju)到來);

· EPOLLERR:表(biao)示對(dui)應的文件描述符發生錯誤;

· EPOLLHUP:表(biao)示(shi)對應的文(wen)件描述(shu)符被掛斷;

· EPOLLET: 將(jiang)EPOLL設為邊緣(yuan)觸發(Edge Triggered)模式,這是(shi)相對于(yu)水平觸發(Level Triggered)來說的;

· EPOLLONESHOT:只監聽一次事(shi)件(jian),當監聽完這(zhe)次事(shi)件(jian)之后,如果還需(xu)要繼續(xu)監聽這(zhe)個(ge)socket的(de)話,需(xu)要再(zai)次把這(zhe)個(ge)socket加入到EPOLL隊列里(li)

返(fan)(fan)回(hui)(hui)值:成功返(fan)(fan)回(hui)(hui)0,失敗返(fan)(fan)回(hui)(hui)-1.

3.2等待(dai)事件---epoll_wait();

int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);

功能:收集在epoll監控的事(shi)件(jian)中已經發(fa)送的事(shi)件(jian)。

參數:

@epfd epoll_create()函數(shu)的返回值(zhi)

@events 已經分(fen)配(pei)好的epoll_event結構體數組(zu),epoll會把將發(fa)生(sheng)的事情存放(fang)到events中。

@maxevents 告訴(su)內核events有多大(da)。必須大(da)于0

@timeout 超時(shi)時(shi)間 -1 表示epoll將(jiang)無限制的等待下去(qu)

0 立即返(fan)回

>0 指定(ding)超(chao)時時間

返回值(zhi): 成功返回已經(jing)就緒(xu)的文件描述符個(ge)數。若是設置了超時時間(jian),在(zai)超時時間(jian)內返回0.

失(shi)敗返回(hui)-1.

五.Epoll的(de)工作模(mo)式。

LT(level triggered)是缺省的工作方式,并且同時(shi)支

持block和no-block socket.在這種做法中,。當epoll_wait檢測到描述符事(shi)件發(fa)生并將(jiang)此事(shi)件通(tong)知(zhi)應(ying)(ying)用(yong)(yong)程序,應(ying)(ying)用(yong)(yong)程序可以不立即處理該事(shi)件。下次調(diao)用(yong)(yong)epoll_wait時,會再次響應(ying)(ying)應(ying)(ying)用(yong)(yong)程序并通(tong)知(zhi)此事(shi)件。

ET (edge-triggered)是高速(su)工作(zuo)(zuo)方式,常工作(zuo)(zuo)在no-block socket。在這種(zhong)模(mo)式下(xia)(xia),當epoll_wait檢測到描述(shu)符事件發生并將此事件通知應(ying)用(yong)程序,應(ying)用(yong)程序必須(xu)立即處理(li)該(gai)事件。如果不處理(li),下(xia)(xia)次調用(yong)epoll_wait時,不會再次響應(ying)應(ying)用(yong)程序并通知此事件。

EPOLLIN事件:

EPOLLIN事件則(ze)只有當(dang)對(dui)端(duan)有數(shu)(shu)據寫(xie)入時才會觸(chu)發,所(suo)以觸(chu)發一次(ci)后需要不(bu)斷(duan)讀取(qu)所(suo)有數(shu)(shu)據直到讀完EAGAIN為止。否則(ze)剩下的(de)數(shu)(shu)據只有在下次(ci)對(dui)端(duan)有寫(xie)入時才能一起取(qu)出來了。設想這樣一個場景(jing):接收端(duan)接收完整(zheng)的(de)數(shu)(shu)據后會向對(dui)端(duan)發送應(ying)答報文(wen),

,對端(duan)才(cai)會繼續向(xiang)接(jie)收端(duan)發(fa)送數(shu)據(ju),從而(er)(er)觸(chu)發(fa)下(xia)一(yi)次的(de)EPOLLIN,而(er)(er)這(zhe)時(shi)沒有(you)讀完(wan)socket緩沖區(qu)中的(de)所有(you)數(shu)據(ju),導(dao)致接(jie)收端(duan)無(wu)法向(xiang)對端(duan)發(fa)送應答報文(wen),而(er)(er)對端(duan)沒有(you)收到應答報文(wen),也(ye)就不會再發(fa)送數(shu)據(ju)觸(chu)發(fa)下(xia)一(yi)次的(de)EPOLLIN,而(er)(er)沒有(you)下(xia)一(yi)次的(de)EPOLLIN事件,接(jie)收端(duan)也(ye)就永遠不知道此socket緩沖區(qu)中還有(you)未讀出的(de)數(shu)據(ju)。一(yi)個完(wan)美的(de)死循環)

示例代碼:

實現多個客戶端和服務端的回射(she)代碼。

Server.c






Client.c





運行結果:

上一篇:想學單片機和嵌入式該怎么學 干貨力薦

下一篇:Kobject 與 sysfs 文件系統框架的分析

熱點文(wen)章推薦
華清(qing)學員就(jiu)業榜單
高薪學員經(jing)驗分享
熱點(dian)新聞推薦
前臺專線:010-82525158 企業培訓洽談專線:010-82525379 院校合作洽談專線:010-82525379 Copyright © 2004-2022 北京華清遠見科技集團有限公司 版權所有 ,,京公海網安備11010802025203號

回到頂部