AIO初步整理(二)——select/poll和epoll

select/poll和epoll

在linux 沒有實(shí)現(xiàn)epoll事件驅(qū)動(dòng)機(jī)制之前,我們一般選擇用select或者poll等IO多路復(fù)用的方法來實(shí)現(xiàn)并發(fā)服務(wù)程序。在大數(shù)據(jù)疲吸、高并發(fā)、集群等一些名詞唱得火熱之年代,select和poll的用武之地越來越有限赃承,風(fēng)頭已經(jīng)被epoll占盡。

select()和poll() IO多路復(fù)用模型

select基本原理

select 函數(shù)監(jiān)視的文件描述符分3類悴侵,分別是writefds瞧剖、readfds、和exceptfds。調(diào)用后select函數(shù)會(huì)阻塞抓于,直到有描述符就緒(有數(shù)據(jù) 可讀做粤、可寫、或者有except)捉撮,或者超時(shí)(timeout指定等待時(shí)間怕品,如果立即返回設(shè)為null即可),函數(shù)返回巾遭。當(dāng)select函數(shù)返回后肉康,可以通過遍歷fdset,來找到就緒的描述符灼舍。

select基本流程

select的缺點(diǎn):

  1. 單個(gè)進(jìn)程能夠監(jiān)視的文件描述符的數(shù)量存在最大限制吼和,通常是1024,當(dāng)然可以更改數(shù)量骑素,但由于select采用輪詢的方式掃描文件描述符炫乓,文件描述符數(shù)量越多,性能越差献丑;

在linux內(nèi)核頭文件中末捣,有這樣的定義:#define __FD_SETSIZE 1024

  1. 內(nèi)核 / 用戶空間內(nèi)存拷貝問題,select需要復(fù)制大量的句柄數(shù)據(jù)結(jié)構(gòu)阳距,產(chǎn)生巨大的開銷塔粒;
  2. select返回的是含有整個(gè)句柄的數(shù)組,應(yīng)用程序需要遍歷整個(gè)數(shù)組才能發(fā)現(xiàn)哪些句柄發(fā)生了事件筐摘;
  3. select的觸發(fā)方式是水平觸發(fā)卒茬,應(yīng)用程序如果沒有完成對一個(gè)已經(jīng)就緒的文件描述符進(jìn)行IO操作,那么之后每次select調(diào)用還是會(huì)將這些文件描述符通知進(jìn)程咖熟。.

poll基本原理

poll本質(zhì)上和select沒有區(qū)別圃酵,它將用戶傳入的數(shù)組拷貝到內(nèi)核空間,然后查詢每個(gè)fd對應(yīng)的設(shè)備狀態(tài)馍管,如果設(shè)備就緒則在設(shè)備等待隊(duì)列中加入一項(xiàng)并繼續(xù)遍歷郭赐,如果遍歷完所有fd后沒有發(fā)現(xiàn)就緒設(shè)備,則掛起當(dāng)前進(jìn)程确沸,直到設(shè)備就緒或者主動(dòng)超時(shí)捌锭,被喚醒后它又要再次遍歷fd。這個(gè)過程經(jīng)歷了多次無謂的遍歷罗捎。

poll的缺點(diǎn):
相比select模型观谦,poll使用鏈表保存文件描述符捉偏,因此沒有了監(jiān)視文件數(shù)量的限制泻红,但其他三個(gè)缺點(diǎn)依然存在讹躯。poll還有一個(gè)特點(diǎn)是“水平觸發(fā)”剩彬,如果報(bào)告了fd后母廷,沒有被處理,那么下次poll時(shí)會(huì)再次報(bào)告該fd抖拦。

epoll IO多路復(fù)用模型

由于epoll的實(shí)現(xiàn)機(jī)制與select/poll機(jī)制完全不同复颈,上面所說的 select的缺點(diǎn)在epoll上不復(fù)存在。epoll是在2.6內(nèi)核中提出的,是之前的select和poll的增強(qiáng)版本。相對于select和poll來說肢执,epoll更加靈活耻陕,沒有描述符限制想诅。epoll使用一個(gè)文件描述符管理多個(gè)描述符徘禁,將用戶關(guān)系的文件描述符的事件存放到內(nèi)核的一個(gè)事件表中骤菠,這樣在用戶空間和內(nèi)核空間的copy只需一次鹉戚。

epoll基本原理

epoll支持水平觸發(fā)和邊緣觸發(fā)赢底,最大的特點(diǎn)在于邊緣觸發(fā)粹庞,它只告訴進(jìn)程哪些fd剛剛變?yōu)榫途w態(tài)咳焚,并且只會(huì)通知一次革半。還有一個(gè)特點(diǎn)是,epoll使用“事件”的就緒通知方式商虐,通過epoll_ctl注冊fd权烧,一旦該fd就緒,內(nèi)核就會(huì)采用類似callback的回調(diào)機(jī)制來激活該fd般码,epoll_wait便可以收到通知宫静。

epoll的優(yōu)點(diǎn):

  1. 沒有最大并發(fā)連接的限制,能打開的FD的上限遠(yuǎn)大于1024(1G的內(nèi)存上能監(jiān)聽約10萬個(gè)端口)券时。
  2. 效率提升孤里,不是輪詢的方式,不會(huì)隨著FD數(shù)目的增加效率下降橘洞。只有活躍可用的FD才會(huì)調(diào)用callback函數(shù)捌袜;即Epoll最大的優(yōu)點(diǎn)就在于它只管你“活躍”的連接,而跟連接總數(shù)無關(guān)炸枣,因此在實(shí)際的網(wǎng)絡(luò)環(huán)境中虏等,Epoll的效率就會(huì)遠(yuǎn)遠(yuǎn)高于select和poll弄唧。
  3. 內(nèi)存拷貝,利用mmap()文件映射內(nèi)存加速與內(nèi)核空間的消息傳遞霍衫;即epoll使用mmap減少復(fù)制開銷套才。

epoll對文件描述符的操作有兩種模式:LT(level trigger)和ET(edge trigger)。LT模式是默認(rèn)模式慕淡,LT模式與ET模式的區(qū)別如下:

  • LT模式:當(dāng)epoll_wait檢測到描述符事件發(fā)生并將此事件通知應(yīng)用程序背伴,應(yīng)用程序可以不立即處理該事件。下次調(diào)用epoll_wait時(shí)峰髓,會(huì)再次響應(yīng)應(yīng)用程序并通知此事件傻寂。
  • ET模式:當(dāng)epoll_wait檢測到描述符事件發(fā)生并將此事件通知應(yīng)用程序,應(yīng)用程序必須立即處理該事件携兵。如果不處理疾掰,下次調(diào)用epoll_wait時(shí),不會(huì)再次響應(yīng)應(yīng)用程序并通知此事件徐紧。

LT和ET模式的優(yōu)缺點(diǎn)

  1. LT模式
    LT(level triggered)是缺省的工作方式静檬,并且同時(shí)支持block和no-block socket。在這種做法中并级,內(nèi)核告訴你一個(gè)文件描述符是否就緒了拂檩,然后你可以對這個(gè)就緒的fd進(jìn)行IO操作。如果你不作任何操作嘲碧,內(nèi)核還是會(huì)繼續(xù)通知你的稻励。
  2. ET模式
    ET(edge-triggered)是高速工作方式,只支持no-block socket愈涩。在這種模式下望抽,當(dāng)描述符從未就緒變?yōu)榫途w時(shí),內(nèi)核通過epoll告訴你履婉。然后它會(huì)假設(shè)你知道文件描述符已經(jīng)就緒煤篙,并且不會(huì)再為那個(gè)文件描述符發(fā)送更多的就緒通知,直到你做了某些操作導(dǎo)致那個(gè)文件描述符不再為就緒狀態(tài)了(比如毁腿,你在發(fā)送辑奈,接收或者接收請求,或者發(fā)送接收的數(shù)據(jù)少于一定量時(shí)導(dǎo)致了一個(gè)EWOULDBLOCK 錯(cuò)誤)狸棍。但是請注意身害,如果一直不對這個(gè)fd作IO操作(從而導(dǎo)致它再次變成未就緒),內(nèi)核不會(huì)發(fā)送更多的通知(only once)草戈。
    ET模式在很大程度上減少了epoll事件被重復(fù)觸發(fā)的次數(shù)塌鸯,因此效率要比LT模式高。epoll工作在ET模式的時(shí)候唐片,必須使用非阻塞套接口丙猬,以避免由于一個(gè)文件句柄的阻塞讀/阻塞寫操作把處理多個(gè)文件描述符的任務(wù)餓死涨颜。
  3. 在select/poll中,進(jìn)程只有在調(diào)用一定的方法后茧球,內(nèi)核才對所有監(jiān)視的文件描述符進(jìn)行掃描庭瑰,而epoll事先通過epoll_ctl()來注冊一個(gè)文件描述符,一旦基于某個(gè)文件描述符就緒時(shí)抢埋,內(nèi)核會(huì)采用類似callback的回調(diào)機(jī)制弹灭,迅速激活這個(gè)文件描述符,當(dāng)進(jìn)程調(diào)用epoll_wait()時(shí)便得到通知揪垄。(此處去掉了遍歷文件描述符穷吮,而是通過監(jiān)聽回調(diào)的的機(jī)制。這正是epoll的魅力所在饥努。)
  • Java NIO對epoll的實(shí)現(xiàn)是LT模式
  • Netty實(shí)現(xiàn)了ET模式的epoll

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

在select/poll時(shí)代,服務(wù)器進(jìn)程每次都把這100萬個(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ò)事件就乓,這一過程資源消耗較大,因此拱烁,select/poll一般只能處理幾千的并發(fā)連接。

epoll的設(shè)計(jì)和實(shí)現(xiàn)與select完全不同噩翠。epoll通過在Linux內(nèi)核中申請一個(gè)簡易的文件系統(tǒng)戏自。把原先的select/poll調(diào)用分成了3個(gè)部分:

  1. 調(diào)用epoll_create()建立一個(gè)epoll對象(在epoll文件系統(tǒng)中為這個(gè)句柄對象分配資源)
  2. 調(diào)用epoll_ctl向epoll對象中添加這100萬個(gè)連接的套接字
  3. 調(diào)用epoll_wait收集發(fā)生的事件的連接

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

下面來看看Linux內(nèi)核具體的epoll機(jī)制實(shí)現(xiàn)思路:

當(dāng)某一進(jìn)程調(diào)用epoll_create方法時(shí),Linux內(nèi)核會(huì)創(chuàng)建一個(gè)eventpoll結(jié)構(gòu)體吉懊,這個(gè)結(jié)構(gòu)體中有兩個(gè)成員與epoll的使用方式密切相關(guān)庐橙。eventpoll結(jié)構(gòu)體如下所示:

struct eventpoll{  
    ....  
    /*紅黑樹的根節(jié)點(diǎn)假勿,這顆樹中存儲(chǔ)著所有添加到epoll中的需要監(jiān)控的事件*/  
    struct rb_root  rbr;  
    /*雙鏈表中則存放著將要通過epoll_wait返回給用戶的滿足條件的事件*/  
    struct list_head rdlist;  
    ....  
};  

每一個(gè)epoll對象都有一個(gè)獨(dú)立的eventpoll結(jié)構(gòu)體,用于存放通過epoll_ctl方法向epoll對象中添加進(jìn)來的事件态鳖。這些事件都會(huì)掛載在紅黑樹中转培,如此,重復(fù)添加的事件就可以通過紅黑樹而高效的識(shí)別出來(紅黑樹的插入時(shí)間效率是lgn浆竭,其中n為樹的高度)浸须。

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

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

struct epitem{  
    struct rb_node  rbn;//紅黑樹節(jié)點(diǎn)  
    struct list_head    rdllink;//雙向鏈表節(jié)點(diǎn)  
    struct epoll_filefd  ffd;  //事件句柄信息  
    struct eventpoll *ep;    //指向其所屬的eventpoll對象  
    struct epoll_event event; //期待發(fā)生的事件類型  
}  

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

epoll數(shù)據(jù)結(jié)構(gòu)示意圖

從上面的講解可知:通過紅黑樹和雙鏈表數(shù)據(jù)結(jié)構(gòu),并結(jié)合回調(diào)機(jī)制硅瞧,造就了epoll的高效份乒。

OK,講解完了Epoll的機(jī)理腕唧,我們便能很容易掌握epoll的用法了或辖。一句話描述就是:三步曲。

  • 第一步:epoll_create()系統(tǒng)調(diào)用枣接。此調(diào)用返回一個(gè)句柄颂暇,之后所有的使用都依靠這個(gè)句柄來標(biāo)識(shí)。
  • 第二步:epoll_ctl()系統(tǒng)調(diào)用但惶。通過此調(diào)用向epoll對象中添加耳鸯、刪除、修改感興趣的事件膀曾,返回0標(biāo)識(shí)成功县爬,返回-1表示失敗。
  • 第三部:epoll_wait()系統(tǒng)調(diào)用添谊。通過此調(diào)用收集收集在epoll監(jiān)控中已經(jīng)發(fā)生的事件财喳。

select/poll和epoll對比

支持一個(gè)進(jìn)程能打開的最大連接數(shù)

image.png

FD劇增后帶來的IO效率

image.png

消息傳體方式

image.png

綜上,在選擇select碉钠,poll纲缓,epoll時(shí)要根據(jù)具體的使用場合以及這三種方式的自身特點(diǎn):

  • 表面上看epoll的性能最好卷拘,但是在連接數(shù)少并且連接都十分活躍的情況下,select和poll的性能可能比epoll好祝高,畢竟epoll的通知機(jī)制需要很多函數(shù)回調(diào)栗弟。
  • select低效是因?yàn)槊看嗡夹枰喸儭5托б彩窍鄬Φ墓す耄暻闆r而定乍赫,也可通過良好的設(shè)計(jì)改善。

DMA
DMA(Direct Memory Access)陆蟆,即直接存儲(chǔ)器存取雷厂,是一種快速傳送數(shù)據(jù)的機(jī)制。數(shù)據(jù)傳遞可以從適配卡到內(nèi)存叠殷,從內(nèi)存到適配卡或從一段內(nèi)存到另一段內(nèi)存改鲫。利用它進(jìn)行數(shù)據(jù)傳送時(shí)不需要CPU的參與。每臺(tái)電腦主機(jī)板上都有DMA控制器林束,通常計(jì)算機(jī)對其編程像棘,并用一個(gè)適配器上的ROM(如軟盤驅(qū)動(dòng)控制器上的ROM)來儲(chǔ)存程序,這些程序控制DMA傳送數(shù)據(jù)壶冒。一旦控制器初始化完成缕题,數(shù)據(jù)開始傳送,DMA就可以脫離CPU胖腾,獨(dú)立完成數(shù)據(jù)傳送烟零。

參考文章
https://blog.csdn.net/davidsguo008/article/details/73556811
https://www.cnblogs.com/jeakeven/p/5435916.html

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市咸作,隨后出現(xiàn)的幾起案子锨阿,更是在濱河造成了極大的恐慌,老刑警劉巖性宏,帶你破解...
    沈念sama閱讀 216,651評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件群井,死亡現(xiàn)場離奇詭異,居然都是意外死亡毫胜,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評論 3 392
  • 文/潘曉璐 我一進(jìn)店門诬辈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來酵使,“玉大人,你說我怎么就攤上這事焙糟】谟妫” “怎么了?”我有些...
    開封第一講書人閱讀 162,931評論 0 353
  • 文/不壞的土叔 我叫張陵穿撮,是天一觀的道長缺脉。 經(jīng)常有香客問我痪欲,道長,這世上最難降的妖魔是什么攻礼? 我笑而不...
    開封第一講書人閱讀 58,218評論 1 292
  • 正文 為了忘掉前任业踢,我火速辦了婚禮,結(jié)果婚禮上礁扮,老公的妹妹穿的比我還像新娘知举。我一直安慰自己,他們只是感情好太伊,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,234評論 6 388
  • 文/花漫 我一把揭開白布雇锡。 她就那樣靜靜地躺著偿乖,像睡著了一般芽淡。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上阳准,一...
    開封第一講書人閱讀 51,198評論 1 299
  • 那天芳悲,我揣著相機(jī)與錄音立肘,去河邊找鬼。 笑死芭概,一個(gè)胖子當(dāng)著我的面吹牛赛不,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播罢洲,決...
    沈念sama閱讀 40,084評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼踢故,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了惹苗?” 一聲冷哼從身側(cè)響起殿较,我...
    開封第一講書人閱讀 38,926評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎桩蓉,沒想到半個(gè)月后淋纲,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,341評論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡院究,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,563評論 2 333
  • 正文 我和宋清朗相戀三年洽瞬,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片业汰。...
    茶點(diǎn)故事閱讀 39,731評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡伙窃,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出样漆,到底是詐尸還是另有隱情为障,我是刑警寧澤,帶...
    沈念sama閱讀 35,430評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站鳍怨,受9級特大地震影響呻右,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜鞋喇,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,036評論 3 326
  • 文/蒙蒙 一声滥、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧确徙,春花似錦醒串、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至伴逸,卻和暖如春缠沈,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背错蝴。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評論 1 269
  • 我被黑心中介騙來泰國打工洲愤, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人顷锰。 一個(gè)月前我還...
    沈念sama閱讀 47,743評論 2 368
  • 正文 我出身青樓柬赐,卻偏偏與公主長得像,于是被迫代替她去往敵國和親官紫。 傳聞我的和親對象是個(gè)殘疾皇子肛宋,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,629評論 2 354

推薦閱讀更多精彩內(nèi)容