I/O多路復(fù)用模型select凡泣,poll枉疼,epoll原理分析及對(duì)比

一、什么是io多路復(fù)用
在bio模型中鞋拟,一個(gè)io請(qǐng)求對(duì)應(yīng)一個(gè)線程骂维,造成線程極大浪費(fèi),且沒有數(shù)據(jù)發(fā)送的情況下贺纲,線程也一直阻塞等待航闺,資源利用率不高。
在nio模型中猴誊,多個(gè)io請(qǐng)求歸一個(gè)線程管理潦刃,當(dāng)io就緒的時(shí)候,線程通知應(yīng)用程序進(jìn)行數(shù)據(jù)讀寫懈叹,這樣不用創(chuàng)建很多線程乖杠,線程也不用一直阻塞等待io就緒,這樣的技術(shù)稱為io多路復(fù)用项阴。
io多路復(fù)用通過一種機(jī)制滑黔,可以監(jiān)視多個(gè)文件描述符,一旦某個(gè)文件描述就緒()环揽,能夠通知應(yīng)用程序進(jìn)行相應(yīng)的io讀寫操作略荡。
二、為什么用io多路復(fù)用
io多路復(fù)用解決了多線程io阻塞問題歉胶,避免創(chuàng)建很多線程汛兜,及降低線程上下文切換的開銷,提升資源利用率通今。
三粥谬、io多路復(fù)用模型原理分析
監(jiān)視多個(gè)文件描述符的機(jī)制不同肛根,io多路復(fù)用技術(shù)主要有select,poll漏策,epoll派哲,本質(zhì)上都是阻塞io,因?yàn)樗麄兌夹枰趇o就緒后掺喻,自己負(fù)責(zé)進(jìn)行數(shù)據(jù)在用戶空間和內(nèi)核空間之間copy砚尽。非阻塞io無需自己負(fù)責(zé)數(shù)據(jù)copy殉挽,操作系統(tǒng)會(huì)把數(shù)據(jù)從內(nèi)核空間copy到用戶空間坡椒。
1.select:int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
監(jiān)視多個(gè)文件描述符的屬性變化(可讀吁脱、可寫、錯(cuò)誤異常)即硼。select監(jiān)視的文件描述符有3類:writefds逃片,readfds,execeptfds只酥,調(diào)用select 線程會(huì)阻塞褥实,知道有描述符就緒,才返回裂允,之后通過遍歷fdset來找到就緒的描述符性锭。
nfds: 要監(jiān)視的文件描述符的范圍,在 Linux 上最大值一般為1024叫胖。
readfd: 監(jiān)視的可讀描述符集合,只要有文件描述符即將進(jìn)行讀操作她奥,這個(gè)文件描述符就存儲(chǔ)到這瓮增。
writefds: 監(jiān)視的可寫描述符集合,只要有文件描述符即將進(jìn)行寫操作哩俭,這個(gè)文件描述符就存儲(chǔ)到這里绷跑。
exceptfds: 監(jiān)視的錯(cuò)誤異常描述符集合。
timeout: 超時(shí)時(shí)間凡资,它告知內(nèi)核等待任何一個(gè)文件描述符就緒可花多少時(shí)間砸捏。有3中設(shè)置:
1)永遠(yuǎn)等待,僅有一個(gè)描述符就緒后返回隙赁;
2)不等待垦藏,檢查所有描述符看其是否就緒,是一種輪訓(xùn)方式伞访,返回就緒的個(gè)數(shù)掂骏;
3)等待固定時(shí)間,返回就緒的個(gè)數(shù)厚掷。
優(yōu)點(diǎn):所有平臺(tái)都支持弟灼,不存在兼容問題级解。
缺點(diǎn):1)每次調(diào)用select,都要把fe集合從用戶態(tài)copy到內(nèi)核態(tài)田绑,fd很多時(shí)勤哗,開銷很大。同時(shí)在內(nèi)核態(tài)要遍歷fd掩驱,看其是否就緒芒划,開銷也很大。
2.poll:int poll(struct pollfd fds, nfds_t nfds, int timeout);
poll的本質(zhì)和select一樣昙篙,沒有太大差別腊状,管理多個(gè)描述符也是進(jìn)行輪詢,根據(jù)描述符狀態(tài)進(jìn)行處理苔可,但poll沒有最大描述符個(gè)數(shù)限制(select限制1024個(gè))缴挖,但是隨著數(shù)量的增加,性能也有下降焚辅。poll() 和 select() 同樣存在一個(gè)缺點(diǎn)就是映屋,包含大量文件描述符的數(shù)組被整體復(fù)制于用戶態(tài)和內(nèi)核的地址空間之間,而不論這些文件描述符是否就緒同蜻,它的開銷隨著文件描述符數(shù)量的增加而線性增大棚点。
fds:不同于 select() ,使用三個(gè)位置來表示三個(gè) fd集合湾蔓,poll() 使用一個(gè) pollfd 的指針瘫析,指向一個(gè) pollfd 結(jié)構(gòu)體數(shù)組,結(jié)構(gòu)體中包括了要監(jiān)視的文件描述符和事件,感興趣的事件由結(jié)構(gòu)中 events 來確定默责,調(diào)用后實(shí)際發(fā)生的時(shí)間將被填寫在結(jié)構(gòu)體的 revents 域中贬循,如下:
struct pollfd{
int fd; /
文件描述符 /
short events; /
感興趣的事件 /
short revents; /
實(shí)際發(fā)生了的事件 */
};
nfds: 指定第一個(gè)參數(shù)數(shù)組元素個(gè)數(shù),并不是限制監(jiān)視的文件描述符個(gè)數(shù)桃序,poll不限制文件描述符個(gè)數(shù)杖虾。
timeout: 指定等待的毫秒數(shù),無論 I/O 是否就緒媒熊,poll() 都會(huì)返回奇适。當(dāng)?shù)却龝r(shí)間為 0 時(shí),poll() 函數(shù)立即返回芦鳍,為 -1 則使 poll() 一直阻塞直到一個(gè)感興趣的事件發(fā)生后返回嚷往。
poll() 返回結(jié)構(gòu)體中 revents 域不為 0 的文件描述符個(gè)數(shù);如果在超時(shí)前沒有任何事件發(fā)生柠衅,poll()返回 0间影;
返回-1說明有錯(cuò)誤發(fā)生,具體錯(cuò)誤有以下集中:
EBADF:一個(gè)或多個(gè)結(jié)構(gòu)體中指定的文件描述符無效。
EFAULT:fds 指針指向的地址超出進(jìn)程的地址空間魂贬。
EINTR:請(qǐng)求的事件之前產(chǎn)生一個(gè)信號(hào)巩割,調(diào)用可以重新發(fā)起。
EINVAL:nfds 參數(shù)超出 PLIMIT_NOFILE 值付燥。
ENOMEM:可用內(nèi)存不足宣谈,無法完成請(qǐng)求。
poll和select非常類似键科,只是fd集合的方式不同闻丑,poll使用結(jié)構(gòu)體數(shù)組,select用set勋颖,其他都差不多嗦嗡。
3.epoll:
int epoll_create(int size);創(chuàng)建一個(gè)epoll文件描述符,管理其他多個(gè)文件描述符饭玲。
size: 用來告訴內(nèi)核這個(gè)監(jiān)聽的數(shù)目一共有多大侥祭,參數(shù) size 并不是限制了 epoll 所能監(jiān)聽的描述符最大個(gè)數(shù),只是對(duì)內(nèi)核初始分配內(nèi)部數(shù)據(jù)結(jié)構(gòu)的一個(gè)建議茄厘。當(dāng)創(chuàng)建好 epoll 描述符后矮冬,它就是會(huì)占用一個(gè) fd 值,在使用完 epoll 后次哈,必須調(diào)用 close() 關(guān)閉胎署,否則可能導(dǎo)致 fd 被耗盡。
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);事件注冊(cè)窑滞,它不同于 select() 是在遍歷描述符時(shí)告訴內(nèi)核要關(guān)注什么事件琼牧,而是在這里先注冊(cè)要關(guān)注的事件。
epfd: epoll 專用的文件描述符哀卫,epoll_create()的返回值
op: 表示動(dòng)作障陶,用三個(gè)宏來表示:
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)聽什么事件恢氯,主要有以下事件:
EPOLLIN :表示對(duì)應(yīng)的文件描述符可以讀(包括對(duì)端 SOCKET 正常關(guān)閉)带斑;
EPOLLOUT:表示對(duì)應(yīng)的文件描述符可以寫;
EPOLLPRI:表示對(duì)應(yīng)的文件描述符有緊急的數(shù)據(jù)可讀勋拟;
EPOLLERR:表示對(duì)應(yīng)的文件描述符發(fā)生錯(cuò)誤勋磕;
EPOLLHUP:表示對(duì)應(yīng)的文件描述符被掛斷;
EPOLLET :將 EPOLL 設(shè)為邊緣觸發(fā)(Edge Triggered)模式敢靡,這是相對(duì)于水平觸發(fā)(Level Triggered)來說的挂滓。
EPOLLONESHOT:只監(jiān)聽一次事件,當(dāng)監(jiān)聽完這次事件之后啸胧,如果還需要繼續(xù)監(jiān)聽這個(gè) socket 的話赶站,需要再次把這個(gè) socket 加入到 EPOLL 隊(duì)列里幔虏。
int epoll_wait( int epfd, struct epoll_event * events, int maxevents, int timeout );注冊(cè)在epoll中已經(jīng)發(fā)生的事件,類似于 select() 調(diào)用贝椿,輪訓(xùn)事件表想括,看是否有事件發(fā)生。
epfd: epoll 專用的文件描述符烙博,epoll_create()的返回值
events:epoll 將會(huì)把發(fā)生的事件添加到events 數(shù)組中瑟蜈,events 不可以是空指針,內(nèi)核只負(fù)責(zé)把數(shù)據(jù)復(fù)制到這個(gè) events 數(shù)組中渣窜,不會(huì)去幫助我們?cè)谟脩魬B(tài)中分配內(nèi)存铺根。
maxevents: maxevents 告之內(nèi)核這個(gè) events 有多大。
timeout: 超時(shí)時(shí)間乔宿,為 -1 時(shí)位迂,函數(shù)為阻塞,知道有事件發(fā)生予颤。
返回就緒的文件描述符個(gè)數(shù)囤官。
epoll 對(duì)文件描述符的操作有兩種模式:LT(level trigger:水平觸發(fā)模式,default)和 ET(edge trigger:邊沿觸發(fā)模式)蛤虐。
LT模式:當(dāng) epoll_wait 檢測(cè)到描述符事件發(fā)生并將此事件通知應(yīng)用程序后党饮,應(yīng)用程序可以不處理該事件,下次調(diào)用 epoll_wait 時(shí)驳庭,會(huì)再次向應(yīng)用程序通知此事件刑顺,直到事件被處理,可能會(huì)重復(fù)處理事件饲常。
ET 模式:當(dāng) epoll_wait 檢測(cè)到描述符事件發(fā)生并將此事件通知應(yīng)用程序后蹲堂,應(yīng)用程序必須立即處理該事件,如果不處理贝淤,下次調(diào)用 epoll_wait 時(shí)柒竞,不會(huì)再次向應(yīng)用程序通知此事件,可能會(huì)丟失事件處理播聪。
在 select/poll中朽基,內(nèi)核對(duì)所有監(jiān)視的文件描述符進(jìn)行掃描,返回就緒的個(gè)數(shù)离陶;而 epoll() 事先通過 epoll_ctl() 來注冊(cè)一個(gè)文件描述符及感興趣的事件稼虎,一旦某個(gè)文件描述符上感興趣的事件發(fā)生后,內(nèi)核會(huì)采用類似 callback 的回調(diào)機(jī)制招刨,將發(fā)生的事件填入epoll_wait()的結(jié)構(gòu)中霎俩,當(dāng)進(jìn)程調(diào)用 epoll_wait() 時(shí)便得到就緒的個(gè)數(shù)。
優(yōu)點(diǎn):
1.監(jiān)視的描述符數(shù)量不受限制。
2.效率不會(huì)隨著監(jiān)視 fd 的數(shù)量的增長而下降打却。select()杉适,poll() 需要自己不斷輪詢所有 fd 集合來發(fā)現(xiàn)就緒設(shè)備,當(dāng)fd集合很大時(shí)学密,開銷很大淘衙。而epoll是通過事件回調(diào)機(jī)制發(fā)現(xiàn)就緒的設(shè)備,當(dāng)設(shè)備就緒后腻暮,回進(jìn)行回調(diào)彤守,不需要輪訓(xùn)。
3.select()哭靖,poll() 每次調(diào)用都要把 fd 集合從用戶態(tài)往內(nèi)核態(tài)拷貝一次具垫,而epoll通過內(nèi)核與用戶空間共享一塊內(nèi)存,避免了無畏的內(nèi)存拷貝(mmap機(jī)制)试幽。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末筝蚕,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子铺坞,更是在濱河造成了極大的恐慌起宽,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,635評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件济榨,死亡現(xiàn)場(chǎng)離奇詭異坯沪,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)擒滑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門腐晾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人丐一,你說我怎么就攤上這事藻糖。” “怎么了库车?”我有些...
    開封第一講書人閱讀 168,083評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵巨柒,是天一觀的道長。 經(jīng)常有香客問我柠衍,道長洋满,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,640評(píng)論 1 296
  • 正文 為了忘掉前任拧略,我火速辦了婚禮,結(jié)果婚禮上瘪弓,老公的妹妹穿的比我還像新娘垫蛆。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評(píng)論 6 397
  • 文/花漫 我一把揭開白布袱饭。 她就那樣靜靜地躺著川无,像睡著了一般。 火紅的嫁衣襯著肌膚如雪虑乖。 梳的紋絲不亂的頭發(fā)上懦趋,一...
    開封第一講書人閱讀 52,262評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音疹味,去河邊找鬼仅叫。 笑死,一個(gè)胖子當(dāng)著我的面吹牛糙捺,可吹牛的內(nèi)容都是我干的诫咱。 我是一名探鬼主播,決...
    沈念sama閱讀 40,833評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼洪灯,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼坎缭!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起签钩,我...
    開封第一講書人閱讀 39,736評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤掏呼,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后铅檩,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體憎夷,經(jīng)...
    沈念sama閱讀 46,280評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評(píng)論 3 340
  • 正文 我和宋清朗相戀三年柠并,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了岭接。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,503評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡臼予,死狀恐怖鸣戴,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情粘拾,我是刑警寧澤窄锅,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站缰雇,受9級(jí)特大地震影響入偷,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜械哟,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評(píng)論 3 333
  • 文/蒙蒙 一疏之、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧暇咆,春花似錦锋爪、人聲如沸丙曙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽亏镰。三九已至,卻和暖如春拯爽,著一層夾襖步出監(jiān)牢的瞬間索抓,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評(píng)論 1 272
  • 我被黑心中介騙來泰國打工毯炮, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留逼肯,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,909評(píng)論 3 376
  • 正文 我出身青樓否副,卻偏偏與公主長得像汉矿,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子备禀,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評(píng)論 2 359

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