寫在前面
轉(zhuǎn)載自: Anker—工作學(xué)習(xí)筆記
參考文章: select、poll除盏、epoll之間的區(qū)別總結(jié)[整理]
select叉橱,poll,epoll
都是IO
多路復(fù)用的機(jī)制者蠕。I/O
多路復(fù)用就通過一種機(jī)制窃祝,可以監(jiān)視多個描述符,一旦某個描述符就緒(一般是讀就緒或者寫就緒)踱侣,能夠通知程序進(jìn)行相應(yīng)的讀寫操作粪小。但select,poll抡句,epoll
本質(zhì)上都是同步I/O
探膊,因為他們都需要在讀寫事件就緒后自己負(fù)責(zé)進(jìn)行讀寫,也就是說這個讀寫過程是阻塞的待榔,而異步I/O
則無需自己負(fù)責(zé)進(jìn)行讀寫逞壁,異步I/O
的實(shí)現(xiàn)會負(fù)責(zé)把數(shù)據(jù)從內(nèi)核拷貝到用戶空間。關(guān)于這三種IO
多路復(fù)用的用法锐锣,前面三篇總結(jié)寫的很清楚腌闯,并用服務(wù)器回射echo
程序進(jìn)行了測試。鏈接如下:
今天對這三種IO多路復(fù)用進(jìn)行對比刺下,參考網(wǎng)上和書上面的資料绑嘹,整理如下:
1. select實(shí)現(xiàn)
select的調(diào)用過程如下所示:
[過程]
(1)使用copy_from_user
從用戶空間拷貝fd_set
到內(nèi)核空間
(2)注冊回調(diào)函數(shù)__pollwait
(3)遍歷所有fd
,調(diào)用其對應(yīng)的poll
方法(對于socket
橘茉,這個poll
方法是sock_poll
,sock_poll
根據(jù)情況會調(diào)用到tcp_poll,udp_poll
或者datagram_poll
)
(4)以tcp_poll
為例姨丈,其核心實(shí)現(xiàn)就是__pollwait
畅卓,也就是上面注冊的回調(diào)函數(shù)。
(5)__pollwait
的主要工作就是把current
(當(dāng)前進(jìn)程)掛到設(shè)備的等待隊列中蟋恬,不同的設(shè)備有不同的等待隊列翁潘,對于tcp_poll
來說,其等待隊列是sk->sk_sleep
(注意把進(jìn)程掛到等待隊列中并不代表進(jìn)程已經(jīng)睡眠了)歼争。在設(shè)備收到一條消息(網(wǎng)絡(luò)設(shè)備)或填寫完文件數(shù)據(jù)(磁盤設(shè)備)后拜马,會喚醒設(shè)備等待隊列上睡眠的進(jìn)程渗勘,這時current
便被喚醒了。
(6)poll
方法返回時會返回一個描述讀寫操作是否就緒的mask
掩碼俩莽,根據(jù)這個mask
掩碼給fd_set
賦值旺坠。
(7)如果遍歷完所有的fd
,還沒有返回一個可讀寫的mask
掩碼扮超,則會調(diào)用schedule_timeout
是調(diào)用select
的進(jìn)程(也就是current
)進(jìn)入睡眠取刃。當(dāng)設(shè)備驅(qū)動發(fā)生自身資源可讀寫后,會喚醒其等待隊列上睡眠的進(jìn)程出刷。如果超過一定的超時時間(schedule_timeout
指定)璧疗,還是沒人喚醒,則調(diào)用select
的進(jìn)程會重新被喚醒獲得CPU馁龟,進(jìn)而重新遍歷fd
崩侠,判斷有沒有就緒的fd
。
(8)把fd_set
從內(nèi)核空間拷貝到用戶空間坷檩。
總結(jié):
select的幾大缺點(diǎn):
(1)每次調(diào)用select啦膜,都需要把fd集合從用戶態(tài)拷貝到內(nèi)核態(tài),這個開銷在fd很多時會很大
(2)同時每次調(diào)用select都需要在內(nèi)核遍歷傳遞進(jìn)來的所有fd淌喻,這個開銷在fd很多時也很大
(3)select支持的文件描述符數(shù)量太小了僧家,默認(rèn)是1024
2. poll實(shí)現(xiàn)
poll
的實(shí)現(xiàn)和select
非常相似,只是描述fd
集合的方式不同裸删,poll
使用pollfd
結(jié)構(gòu)而不是select
的fd_set
結(jié)構(gòu)八拱,其他的都差不多。
關(guān)于select和poll的實(shí)現(xiàn)分析涯塔,可以參考下面幾篇博文:
http://blog.csdn.net/lizhiguo0532/article/details/6568964#comments
http://blog.csdn.net/lizhiguo0532/article/details/6568968
http://blog.csdn.net/lizhiguo0532/article/details/6568969
http://www.ibm.com/developerworks/cn/linux/l-cn-edntwk/index.html?ca=drs-
http://linux.chinaunix.net/techdoc/net/2009/05/03/1109887.shtml
3. epoll
epoll
既然是對select
和poll
的改進(jìn)肌稻,就應(yīng)該能避免上述的三個缺點(diǎn)。那epoll
都是怎么解決的呢匕荸?在此之前爹谭,我們先看一下epoll
和select
和poll
的調(diào)用接口上的不同,select
和poll
都只提供了一個函數(shù)——select
或者poll
函數(shù)榛搔。而epoll
提供了三個函數(shù):epoll_create,epoll_ctl 和 epoll_wait
诺凡,epoll_create
是創(chuàng)建一個epoll句柄;epoll_ctl
是注冊要監(jiān)聽的事件類型践惑;epoll_wait
則是等待事件的產(chǎn)生腹泌。
對于第一個缺點(diǎn),epoll
的解決方案在epoll_ct
l函數(shù)中:每次注冊新的事件到epoll
句柄中時(在epoll_ctl
中指定EPOLL_CTL_ADD
)尔觉,會把所有的fd
拷貝進(jìn)內(nèi)核凉袱,而不是在epoll_wait
的時候重復(fù)拷貝。epoll
保證了每個fd
在整個過程中只會拷貝一次。
對于第二個缺點(diǎn)专甩,epoll
的解決方案不像select
或poll
一樣每次都把current
輪流加入fd
對應(yīng)的設(shè)備等待隊列中钟鸵,而只在epoll_ctl
時把current
掛一遍(這一遍必不可少)并為每個fd
指定一個回調(diào)函數(shù),當(dāng)設(shè)備就緒涤躲,喚醒等待隊列上的等待者時棺耍,就會調(diào)用這個回調(diào)函數(shù),而這個回調(diào)函數(shù)會把就緒的fd
加入一個就緒鏈表)篓叶。epoll_wait
的工作實(shí)際上就是在這個就緒鏈表中查看有沒有就緒的fd
(利用schedule_timeout()
實(shí)現(xiàn)睡一會烈掠,判斷一會的效果,和selec
t實(shí)現(xiàn)中的第7步是類似的)缸托。
對于第三個缺點(diǎn)左敌,epoll
沒有這個限制,它所支持的FD
上限是最大可以打開文件的數(shù)目俐镐,這個數(shù)字一般遠(yuǎn)大于2048
,舉個例子:在1GB內(nèi)存的機(jī)器上大約是10萬左右矫限,具體數(shù)目可以cat /proc/sys/fs/file-max
察看,一般來說這個數(shù)目和系統(tǒng)內(nèi)存關(guān)系很大。
總結(jié):
(1)select佩抹,poll實(shí)現(xiàn)需要自己不斷輪詢所有fd集合叼风,直到設(shè)備就緒,期間可能要睡眠和喚醒多次交替棍苹。而epoll其實(shí)也需要調(diào)用epoll_wait不斷輪詢就緒鏈表无宿,期間也可能多次睡眠和喚醒交替,但是它是設(shè)備就緒時枢里,調(diào)用回調(diào)函數(shù)孽鸡,把就緒fd放入就緒鏈表中,并喚醒在epoll_wait中進(jìn)入睡眠的進(jìn)程栏豺。雖然都要睡眠和交替彬碱,但是select和poll在“醒著”的時候要遍歷整個fd集合,而epoll在“醒著”的時候只要判斷一下就緒鏈表是否為空就行了奥洼,這節(jié)省了大量的CPU時間巷疼。這就是回調(diào)機(jī)制帶來的性能提升。
(2)select灵奖,poll每次調(diào)用都要把fd集合從用戶態(tài)往內(nèi)核態(tài)拷貝一次嚼沿,并且要把current往設(shè)備等待隊列中掛一次,而epoll只要一次拷貝桑寨,而且把current往等待隊列上掛也只掛一次(在epoll_wait的開始伏尼,注意這里的等待隊列并不是設(shè)備等待隊列,只是一個epoll內(nèi)部定義的等待隊列)尉尾。這也能節(jié)省不少的開銷。
參考資料:
http://www.cnblogs.com/apprentice89/archive/2013/05/09/3070051.html
http://www.linuxidc.com/Linux/2012-05/59873p3.htm
http://xingyunbaijunwei.blog.163.com/blog/static/76538067201241685556302/
http://blog.csdn.net/kkxgx/article/details/7717125
https://banu.com/blog/2/how-to-use-epoll-a-complete-example-in-c/epoll-example.c