国产成人精品三级麻豆,色综合天天综合高清网,亚洲精品夜夜夜,国产成人综合在线女婷五月99播放,色婷婷色综合激情国产日韩

當(dāng)前位置:IT培訓(xùn) > 華清遠(yuǎn)見教育科技集團(tuán) > 嵌入式學(xué)習(xí) > 講師博文 > EPOLL的工作原理及流程
EPOLL的工作原理及流程
時(shí)間:2018-05-14作者:華清遠(yuǎn)見

一.Epoll是什么?

epoll是個(gè)什么東東呢?按照man手冊(cè)的說(shuō)法:是為處理大批量句柄而作了改進(jìn)的poll。當(dāng)然,這不是2.6內(nèi)核才有的,它是在2.5.44內(nèi)核中被引進(jìn)的(epoll(4) is a new API introduced in Linux kernel 2.5.44),它幾乎具備了之前所說(shuō)的一切優(yōu)點(diǎn),被公認(rèn)為L(zhǎng)inux2.6下性能最好的多路I/O就緒通知方法。

二.epoll與poll和select對(duì)比

[1]select 的缺點(diǎn):

單個(gè)進(jìn)程能夠監(jiān)視的文件描述符的數(shù)量存在最大限制,通常是1024,當(dāng)然可以更改數(shù)量,但由于select采用輪詢的方式掃描文件描述符,文件描述符數(shù)量越多,性能越差;(在linux內(nèi)核頭文件中,有這樣的定義:#define __FD_SETSIZE 1024)

內(nèi)核 / 用戶空間內(nèi)存拷貝問(wèn)題,select需要復(fù)制大量的句柄數(shù)據(jù)結(jié)構(gòu),產(chǎn)生巨大的開銷;

select返回的是含有整個(gè)句柄的數(shù)組,應(yīng)用程序需要遍歷整個(gè)數(shù)組才能發(fā)現(xiàn)哪些句柄發(fā)生了事件;

select中應(yīng)用程序如果沒(méi)有完成對(duì)一個(gè)已經(jīng)就緒的文件描述符進(jìn)行IO操作,那么之后每次select調(diào)用還是會(huì)將這些文件描述符通知進(jìn)程。

相對(duì)于我們的select模型,我們的poll是使用鏈表保持文件描述符,因此沒(méi)有了監(jiān)視文件數(shù)量的限制,但是2,3,4等缺點(diǎn)依舊存在。

拿select模型為例,假設(shè)我們的服務(wù)器需要支持100萬(wàn)的并發(fā)連接,則在__FD_SETSIZE 為1024的情況下,則我們至少需要開辟1k個(gè)進(jìn)程才能實(shí)現(xiàn)100萬(wàn)的并發(fā)連接。除了進(jìn)程間上下文切換的時(shí)間消耗外,從內(nèi)核/用戶空間大量的無(wú)腦內(nèi)存拷貝、數(shù)組輪詢等,是系統(tǒng)難以承受的。因此,基于select模型的服務(wù)器程序,要達(dá)到10萬(wàn)級(jí)別的并發(fā)訪問(wèn),是一個(gè)很難完成的任務(wù)。

因此,該epoll上場(chǎng)了。

三.Epoll的工作原理

設(shè)想一下如下場(chǎng)景:有100萬(wàn)個(gè)客戶端同時(shí)與一個(gè)服務(wù)器進(jìn)程保持著TCP連接。而每一時(shí)刻,通常只有幾百上千個(gè)TCP連接是活躍的(事實(shí)上大部分場(chǎng)景都是這種情況)。如何實(shí)現(xiàn)這樣的高并發(fā)?

在select/poll時(shí)代,服務(wù)器進(jìn)程每次都把這100萬(wàn)個(gè)連接告訴操作系統(tǒng)(從用戶態(tài)復(fù)制句柄數(shù)據(jù)結(jié)構(gòu)到內(nèi)核態(tài)),讓操作系統(tǒng)內(nèi)核去查詢這些套接字上是否有事件發(fā)生,輪詢完后,再將句柄數(shù)據(jù)復(fù)制到用戶態(tài),讓服務(wù)器應(yīng)用程序輪詢處理已發(fā)生的網(wǎng)絡(luò)事件,這一過(guò)程資源消耗較大,因此,select/poll一般只能處理幾千的并發(fā)連接。

epoll的設(shè)計(jì)和實(shí)現(xiàn)與select完全不同。epoll通過(guò)在Linux內(nèi)核中申請(qǐng)一個(gè)簡(jiǎn)易的文件系統(tǒng)(文件系統(tǒng)一般用什么數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)?二叉樹樹)。然后epoll的調(diào)用分成了3個(gè)部分:

1)調(diào)用epoll_create()建立一個(gè)epoll對(duì)象(在epoll文件系統(tǒng)中為這個(gè)句柄對(duì)象分配資源)

2)調(diào)用epoll_ctl向epoll對(duì)象中添加這100萬(wàn)個(gè)連接的套接字

3)調(diào)用epoll_wait收集發(fā)生的事件的連接

如此一來(lái),要實(shí)現(xiàn)上面說(shuō)是的場(chǎng)景,只需要在進(jìn)程啟動(dòng)時(shí)建立一個(gè)epoll對(duì)象,然后在需要的時(shí)候向這個(gè)epoll對(duì)象中添加或者刪除連接。同時(shí),epoll_wait的效率也非常高,因?yàn)檎{(diào)用epoll_wait時(shí),并沒(méi)有一股腦的向操作系統(tǒng)復(fù)制這100萬(wàn)個(gè)連接的句柄數(shù)據(jù),內(nèi)核也不需要去遍歷全部的連接。

具體流程:

[1]當(dāng)我們某個(gè)進(jìn)程調(diào)用epoll_create()函數(shù)的時(shí)候,linux內(nèi)核會(huì)默認(rèn)創(chuàng)建一個(gè)eventpoll結(jié)構(gòu)體,這個(gè)結(jié)構(gòu)體中有兩個(gè)成員與epoll的使用方式相關(guān)。

每一個(gè)epoll對(duì)象都有一個(gè)獨(dú)立的eventpoll結(jié)構(gòu)體,用于存放通過(guò)epoll_ctl方法向epoll對(duì)象中添加進(jìn)來(lái)的事件。這些事件都會(huì)掛載在紅黑樹中,如此,重復(fù)添加的事件就可以通過(guò)紅黑樹而高效的識(shí)別出來(lái).

而所有添加到epoll中的事件都會(huì)與設(shè)備(網(wǎng)卡)驅(qū)動(dòng)程序建立回調(diào)關(guān)系,也就是說(shuō),當(dāng)相應(yīng)的事件發(fā)生時(shí)會(huì)調(diào)用這個(gè)回調(diào)方法。這個(gè)回調(diào)方法在內(nèi)核中叫ep_poll_callback,它會(huì)將發(fā)生的事件epitem添加到rdlist雙鏈表中。

在epoll中,對(duì)于每一個(gè)事件,都會(huì)建立一個(gè)epitem結(jié)構(gòu)體,如下所示:

當(dāng)調(diào)用epoll_wait檢查是否有事件發(fā)生時(shí),只需要檢查eventpoll對(duì)象中的rdlist雙鏈表中是否有epitem元素即可。如果rdlist不為空,則把發(fā)生的事件復(fù)制到用戶態(tài),同時(shí)將事件數(shù)量返回給用戶。

總結(jié):

(1)我們我們調(diào)用epoll_wait()函數(shù)的時(shí)候,系統(tǒng)創(chuàng)建一個(gè)epoll對(duì)象,每個(gè)對(duì)象都有一個(gè)

叫做eventpoll類型的結(jié)構(gòu)體與之對(duì)應(yīng),該結(jié)構(gòu)體中主要有兩個(gè)主要的成員,一個(gè)是

rbn,代表將要通過(guò)epoll_ctl向epll對(duì)象中添加的事件。這些事情都是掛載在紅黑樹中。

一個(gè)是rdlist,里面存放的是將要發(fā)生的事件

(2)當(dāng)我們使用epoll_ctrl()函數(shù)的時(shí)候,就是向epoll對(duì)象中添加,刪除,修改感興趣的事件

(3) epoll_wait()系統(tǒng)。通過(guò)此調(diào)用收集在epoll監(jiān)控中已經(jīng)發(fā)生的事件。當(dāng)監(jiān)控的事件狀態(tài)發(fā)生改變的時(shí)候,我們會(huì)調(diào)用會(huì)調(diào)用函數(shù)把epitem加入到rdlist中去。

一. Epoll的API函數(shù)接口

3.1 事件的創(chuàng)建---epoll_create();

int epoll_create(int size);

int epoll_create1(int flags);

功能:poll_create()創(chuàng)建一個(gè)epoll的事例,通知內(nèi)核需要監(jiān)聽size個(gè)fd。size指的并不是最大的后備存儲(chǔ)設(shè)備,而是衡量?jī)?nèi)核內(nèi)部結(jié)構(gòu)大小的一個(gè)提示。當(dāng)創(chuàng)建成功后,會(huì)占用一個(gè)fd,所以記得在使用完之后調(diào)用close(),否則fd可能會(huì)被耗盡。

Note:自從Linux2.6.8版本以后,size值其實(shí)是沒(méi)什么用的,不過(guò)要大于0,因?yàn)閮?nèi)核可以動(dòng)態(tài)的分配大小,所以不需要size這個(gè)提示了。

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

· 當(dāng)flag是0時(shí),表示和epoll_create函數(shù)完全一樣,不需要size的提示了;

· 當(dāng)flag = EPOLL_CLOEXEC,創(chuàng)建的epfd會(huì)設(shè)置FD_CLOEXEC;

· 當(dāng)flag = EPOLL_NONBLOCK,創(chuàng)建的epfd會(huì)設(shè)置為非阻塞。

一般用法都是使用EPOLL_CLOEXEC。

Note:關(guān)于FD_CLOEXEC,它是fd的一個(gè)標(biāo)識(shí)說(shuō)明,用來(lái)設(shè)置文件close-on-exec狀態(tài)的。當(dāng)close-on-exec狀態(tài)為0時(shí),調(diào)用exec時(shí),fd不會(huì)被關(guān)閉;狀態(tài)非零時(shí)則會(huì)被關(guān)閉,這樣做可以防止fd泄露給執(zhí)行exec后的進(jìn)程。

返回值:成功返回一個(gè)非負(fù)的文件描述符。

例如:

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

int epfd = epoll_create1(0);

3.1 事件的注冊(cè)---epoll_ctl();

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

功能:epoll的事件注冊(cè)函數(shù),epoll的事件注冊(cè)函數(shù),它不同于select()是在監(jiān)聽事件時(shí)告訴內(nèi)核要監(jiān)聽什么類型的事件,而是在這里先注冊(cè)要監(jiān)聽的事件類型。

參數(shù):

@epfd epoll_create()函數(shù)的返回值

@op 表示參數(shù)的動(dòng)作,常用以下宏:

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

EPOLL_CTL_MOD:修改已經(jīng)注冊(cè)的fd的監(jiān)聽事件;

EPOLL_CTL_DEL:從epfd中刪除一個(gè)fd;

@fd 表示我們需要監(jiān)聽的文件描述符

@event 表示告訴內(nèi)核,我們需要監(jiān)聽什么事件。

結(jié)構(gòu)體如下:

typedef union epoll_data

{

void *ptr;

int fd; //保存我們使用的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參數(shù)是一個(gè)枚舉的集合,可以用” | “來(lái)增加事件類型,枚舉如下:

· EPOLLIN :表示對(duì)應(yīng)的文件描述符可以讀(包括對(duì)端SOCKET正常關(guān)閉);

· EPOLLOUT:表示對(duì)應(yīng)的文件描述符可以寫;

· EPOLLPRI:表示對(duì)應(yīng)的文件描述符有緊急的數(shù)據(jù)可讀(這里應(yīng)該表示有帶外數(shù)據(jù)到來(lái));

· EPOLLERR:表示對(duì)應(yīng)的文件描述符發(fā)生錯(cuò)誤;

· EPOLLHUP:表示對(duì)應(yīng)的文件描述符被掛斷;

· EPOLLET: 將EPOLL設(shè)為邊緣觸發(fā)(Edge Triggered)模式,這是相對(duì)于水平觸發(fā)(Level Triggered)來(lái)說(shuō)的;

· EPOLLONESHOT:只監(jiān)聽一次事件,當(dāng)監(jiān)聽完這次事件之后,如果還需要繼續(xù)監(jiān)聽這個(gè)socket的話,需要再次把這個(gè)socket加入到EPOLL隊(duì)列里

返回值:成功返回0,失敗返回-1.

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

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

功能:收集在epoll監(jiān)控的事件中已經(jīng)發(fā)送的事件。

參數(shù):

@epfd epoll_create()函數(shù)的返回值

@events 已經(jīng)分配好的epoll_event結(jié)構(gòu)體數(shù)組,epoll會(huì)把將發(fā)生的事情存放到events中。

@maxevents 告訴內(nèi)核events有多大。必須大于0

@timeout 超時(shí)時(shí)間 -1 表示epoll將無(wú)限制的等待下去

0 立即返回

>0 指定超時(shí)時(shí)間

返回值: 成功返回已經(jīng)就緒的文件描述符個(gè)數(shù)。若是設(shè)置了超時(shí)時(shí)間,在超時(shí)時(shí)間內(nèi)返回0.

失敗返回-1.

五.Epoll的工作模式。

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

持block和no-block socket.在這種做法中,。當(dāng)epoll_wait檢測(cè)到描述符事件發(fā)生并將此事件通知應(yīng)用程序,應(yīng)用程序可以不立即處理該事件。下次調(diào)用epoll_wait時(shí),會(huì)再次響應(yīng)應(yīng)用程序并通知此事件。

ET (edge-triggered)是高速工作方式,常工作在no-block socket。在這種模式下,當(dāng)epoll_wait檢測(cè)到描述符事件發(fā)生并將此事件通知應(yīng)用程序,應(yīng)用程序必須立即處理該事件。如果不處理,下次調(diào)用epoll_wait時(shí),不會(huì)再次響應(yīng)應(yīng)用程序并通知此事件。

EPOLLIN事件:

EPOLLIN事件則只有當(dāng)對(duì)端有數(shù)據(jù)寫入時(shí)才會(huì)觸發(fā),所以觸發(fā)一次后需要不斷讀取所有數(shù)據(jù)直到讀完EAGAIN為止。否則剩下的數(shù)據(jù)只有在下次對(duì)端有寫入時(shí)才能一起取出來(lái)了。設(shè)想這樣一個(gè)場(chǎng)景:接收端接收完整的數(shù)據(jù)后會(huì)向?qū)Χ税l(fā)送應(yīng)答報(bào)文,

,對(duì)端才會(huì)繼續(xù)向接收端發(fā)送數(shù)據(jù),從而觸發(fā)下一次的EPOLLIN,而這時(shí)沒(méi)有讀完socket緩沖區(qū)中的所有數(shù)據(jù),導(dǎo)致接收端無(wú)法向?qū)Χ税l(fā)送應(yīng)答報(bào)文,而對(duì)端沒(méi)有收到應(yīng)答報(bào)文,也就不會(huì)再發(fā)送數(shù)據(jù)觸發(fā)下一次的EPOLLIN,而沒(méi)有下一次的EPOLLIN事件,接收端也就永遠(yuǎn)不知道此socket緩沖區(qū)中還有未讀出的數(shù)據(jù)。一個(gè)完美的死循環(huán))

示例代碼:

實(shí)現(xiàn)多個(gè)客戶端和服務(wù)端的回射代碼。

Server.c






Client.c





運(yùn)行結(jié)果:


發(fā)表評(píng)論

全國(guó)咨詢電話:400-611-6270,雙休日及節(jié)假日請(qǐng)致電值班手機(jī):15010390966

在線咨詢: 曹老師QQ(3337544669), 徐老師QQ(1462495461), 劉老師 QQ(3108687497)

企業(yè)培訓(xùn)洽談專線:010-82600901,院校合作洽談專線:010-82600350,在線咨詢:QQ(248856300)

Copyright 2004-2018 華清遠(yuǎn)見教育科技集團(tuán) 版權(quán)所有 ,京ICP備16055225號(hào),京公海網(wǎng)安備11010802025203號(hào)

有位老師想和您聊一聊