真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

三種多路復(fù)用IO實(shí)現(xiàn)方式:select,poll,epoll的區(qū)別

select,poll,epoll都是IO多路復(fù)用的機(jī)制。I/O多路復(fù)用就通過(guò)一種機(jī)制,可以監(jiān)視多個(gè)描述符,一旦某個(gè)描述符就緒(一般是讀就緒或者寫(xiě)就緒),能夠通知程序進(jìn)行相應(yīng)的讀寫(xiě)操作。但select,poll,epoll本質(zhì)上都是同步I/O,因?yàn)樗麄兌夹枰谧x寫(xiě)事件就緒后自己負(fù)責(zé)進(jìn)行讀寫(xiě),也就是說(shuō)這個(gè)讀寫(xiě)過(guò)程是阻塞的,而異步I/O則無(wú)需自己負(fù)責(zé)進(jìn)行讀寫(xiě),異步I/O的實(shí)現(xiàn)會(huì)負(fù)責(zé)把數(shù)據(jù)從內(nèi)核拷貝到用戶空間。

成都創(chuàng)新互聯(lián)公司-專(zhuān)業(yè)網(wǎng)站定制、快速模板網(wǎng)站建設(shè)、高性價(jià)比高坪網(wǎng)站開(kāi)發(fā)、企業(yè)建站全套包干低至880元,成熟完善的模板庫(kù),直接使用。一站式高坪網(wǎng)站制作公司更省心,省錢(qián),快速模板網(wǎng)站建設(shè)找我們,業(yè)務(wù)覆蓋高坪地區(qū)。費(fèi)用合理售后完善,十載實(shí)體公司更值得信賴(lài)。

此時(shí)需知道兩個(gè)概念:

所謂阻塞方式block,顧名思義,就是進(jìn)程或是線程執(zhí)行到這些函數(shù)時(shí)必須等待某個(gè)事件的發(fā)生,如果事件沒(méi)有發(fā)生,進(jìn)程或線程就被阻塞,函數(shù)不能立即返回。

所謂非阻塞方式non-block,就是進(jìn)程或線程執(zhí)行此函數(shù)時(shí)不必非要等待事件的發(fā)生,一旦執(zhí)行肯定返回,以返回值的不同來(lái)反映函數(shù)的執(zhí)行情況,如果事件發(fā)生則與阻塞方式相同,若事件沒(méi)有發(fā)生,則返回一個(gè)代碼來(lái)告知事件未發(fā)生,而進(jìn)程或線程繼續(xù)執(zhí)行,所以效率較高。

一.select()的機(jī)制中提供一fd_set的數(shù)據(jù)結(jié)構(gòu),實(shí)際上是一long類(lèi)型的數(shù)組, 每一個(gè)數(shù)組元素都能與一打開(kāi)的文件句柄(不管是Socket句柄,還是其他 文件或命名管道或設(shè)備句柄)建立聯(lián)系,建立聯(lián)系的工作由程序員完成, 當(dāng)調(diào)用select()時(shí),由內(nèi)核根據(jù)IO狀態(tài)修改fd_set的內(nèi)容,由此來(lái)通知執(zhí)行了select()的進(jìn)程哪一Socket或文件可讀或可寫(xiě)。主要用于Socket通信當(dāng)中。

select使用:它能夠監(jiān)視我們需要監(jiān)視的文件描述符的變化情況——讀寫(xiě)或是異常。準(zhǔn)備就緒的描述符數(shù),若超時(shí)則返回0,若出錯(cuò)則返回-1。

1.如果一個(gè)發(fā)現(xiàn)I/O有輸入,讀取的過(guò)程中,另外一個(gè)也有了輸入,這時(shí)候不會(huì)產(chǎn)生任何反應(yīng).這就需要你的程序語(yǔ)句去用到select函數(shù)的時(shí)候才知道有數(shù)據(jù)輸入。

2.程序去select的時(shí)候,如果沒(méi)有數(shù)據(jù)輸入,程序會(huì)一直等待(阻塞時(shí)),直到有數(shù)據(jù)為止,也就是程序中無(wú)需循環(huán)和sleep。

#include

#include

#include

int select(int nfds, fd_set * readfds, fd_set * writefds, fd_set * exceptfds, struct timeval * timeout)

函數(shù)返回結(jié)果:當(dāng)readfds或writefds中映象的文件可讀或可寫(xiě)或超時(shí),本次select()就結(jié)束返回。程序員利用一組系統(tǒng)提供的宏在select()結(jié)束時(shí)便可判斷哪一文件可讀或可寫(xiě),對(duì)Socket編程特別有用的就是readfds。

注:不同的timeval設(shè)置使select()表現(xiàn)出超時(shí)結(jié)束、無(wú)超時(shí)阻塞和輪詢?nèi)N特性(timeval可精確至百萬(wàn)分之一秒)。

select詳細(xì)執(zhí)行步驟:

三種多路復(fù)用IO實(shí)現(xiàn)方式:select,poll,epoll的區(qū)別

(1)使用copy_from_user從用戶空間拷貝fd_set到內(nèi)核空間

(2)注冊(cè)回調(diào)函數(shù)__pollwait

(3)遍歷所有fd,調(diào)用其對(duì)應(yīng)的poll方法(對(duì)于socket,這個(gè)poll方法是sock_poll,sock_poll根據(jù)情況會(huì)調(diào)用到tcp_poll,udp_poll或者datagram_poll)

(4)以tcp_poll為例,其核心實(shí)現(xiàn)就是__pollwait,也就是上面注冊(cè)的回調(diào)函數(shù)。

(5)__pollwait的主要工作就是把current(當(dāng)前進(jìn)程)掛到設(shè)備的等待隊(duì)列中,不同的設(shè)備有不同的等待隊(duì)列,對(duì)于tcp_poll來(lái)說(shuō),其等待隊(duì)列是sk->sk_sleep(注意把進(jìn)程掛到等待隊(duì)列中并不代表進(jìn)程已經(jīng)睡眠了)。在設(shè)備收到一條消息(網(wǎng)絡(luò)設(shè)備)或填寫(xiě)完文件數(shù)據(jù)(磁盤(pán)設(shè)備)后,會(huì)喚醒設(shè)備等待隊(duì)列上睡眠的進(jìn)程,這時(shí)current便被喚醒了。

(6)poll方法返回時(shí)會(huì)返回一個(gè)描述讀寫(xiě)操作是否就緒的mask掩碼,根據(jù)這個(gè)mask掩碼給fd_set賦值。

(7)如果遍歷完所有的fd,還沒(méi)有返回一個(gè)可讀寫(xiě)的mask掩碼,則會(huì)調(diào)用schedule_timeout是調(diào)用select的進(jìn)程(也就是current)進(jìn)入睡眠。當(dāng)設(shè)備驅(qū)動(dòng)發(fā)生自身資源可讀寫(xiě)后,會(huì)喚醒其等待隊(duì)列上睡眠的進(jìn)程。如果超過(guò)一定的超時(shí)時(shí)間(schedule_timeout指定),還是沒(méi)人喚醒,則調(diào)用select的進(jìn)程會(huì)重新被喚醒獲得CPU,進(jìn)而重新遍歷fd,判斷有沒(méi)有就緒的fd。

(8)把fd_set從內(nèi)核空間拷貝到用戶空間。

從以上工作流程可得到select特點(diǎn):

a.所監(jiān)視的每種事件描述符個(gè)數(shù)有上限;

printf("%d\n",sizeof(fd_set));

我的linux系統(tǒng)所能關(guān)心事件應(yīng)為128字節(jié)*8=1024個(gè)描述符

三種多路復(fù)用IO實(shí)現(xiàn)方式:select,poll,epoll的區(qū)別

b.調(diào)用前后輪詢;

使用select函數(shù),必須使用輔助數(shù)組保存關(guān)心的描述符,因?yàn)閟elect函數(shù)中描述符集是輸入輸出型參數(shù),故在調(diào)用前應(yīng)輪詢數(shù)組重置描述符集,調(diào)用后得輪詢描述符集判斷關(guān)心事件是否就緒。

c.系統(tǒng)與用戶數(shù)據(jù)拷貝:使用copy_from_user從用戶空間拷貝fd_set到內(nèi)核空間。

d.調(diào)用前需重置(描述符集是輸入輸出型參數(shù))。

二.poll:

poll的實(shí)現(xiàn)和select非常相似,只是描述fd集合的方式不同,poll使用pollfd結(jié)構(gòu)而不是select的fd_set結(jié)構(gòu),其他的都差不多。

#include

int poll(struct pollfd fds[], nfds_t nfds, int timeout);

監(jiān)視描述符事件選項(xiàng):

三種多路復(fù)用IO實(shí)現(xiàn)方式:select,poll,epoll的區(qū)別

fds:是一個(gè)struct pollfd結(jié)構(gòu)類(lèi)型的數(shù)組,用于存放需要檢測(cè)其狀態(tài)的Socket描述符;每當(dāng)調(diào)用這個(gè)函數(shù)之后,系統(tǒng)不會(huì)清空這個(gè)數(shù)組,操作起來(lái)比較方便;特別是對(duì)于socket連接比較多的情況下,在一定程度上可以提高處理的效率;這一點(diǎn)與select()函數(shù)不同,調(diào)用select()函數(shù)之后,select()函數(shù)會(huì)清空它所檢測(cè)的socket描述符集合,導(dǎo)致每次調(diào)用select()之前都必須把socket描述符重新加入到待檢測(cè)的集合中;因此,select()函數(shù)適合于只檢測(cè)一個(gè)socket描述符的情況,而poll()函數(shù)適合于大量socket描述符的情況;與select()十分相似,當(dāng)返回正值時(shí),代表滿足響應(yīng)事件的文件描述符的個(gè)數(shù),如果返回0則代表在規(guī)定時(shí)間內(nèi)沒(méi)有事件發(fā)生。如發(fā)現(xiàn)返回為負(fù)則應(yīng)該立即查看 errno,因?yàn)檫@代表有錯(cuò)誤發(fā)生。

注:如果沒(méi)有事件發(fā)生,revents會(huì)被清空。

poll特點(diǎn):

  1. 監(jiān)視描述符個(gè)數(shù)無(wú)上限;

最大描述符+1,個(gè)數(shù)由fds數(shù)組決定。

2.監(jiān)視事件與返回后事件狀態(tài)反生分離,調(diào)用前后不需重置。

3.調(diào)用后輪詢檢測(cè)監(jiān)視事件是否發(fā)生。

4.系統(tǒng)與用戶數(shù)據(jù)拷貝:使用copy_from_user從用戶空間拷貝fds到內(nèi)核空間

三.epoll.

epoll是linux內(nèi)核為處理大批量文件描述符而作了改進(jìn)的poll,是Linux下多路復(fù)用IO接口select/poll的增強(qiáng)版本,它能顯著提高程序在大量并發(fā)連接中中只有少量活躍的情況下的系統(tǒng)CPU利用率。另一點(diǎn)原因就是獲取事件的時(shí)候,它無(wú)須遍歷整個(gè)被偵聽(tīng)的描述符集,只要遍歷那些被內(nèi)核IO事件異步喚醒而加入Ready隊(duì)列的描述符集合就行了。epoll除了提供select/poll那種IO事件的水平觸發(fā)(Level Triggered)外,還提供了邊緣觸發(fā)(Edge Triggered),這就使得用戶空間程序有可能緩存IO狀態(tài),減少epoll_wait/epoll_pwait的調(diào)用,提高應(yīng)用程序效率。

epoll特點(diǎn):

1.epoll和select和poll的調(diào)用接口上的不同。

select和poll都只提供了一個(gè)函數(shù)——select或者poll函數(shù)。而epoll提供了三個(gè)函數(shù),epoll_create,epoll_ctl和epoll_wait,epoll_create是創(chuàng)建一個(gè)epoll句柄;epoll_ctl是注冊(cè)要監(jiān)聽(tīng)的事件類(lèi)型;epoll_wait則是等待事件的產(chǎn)生。

2.使用mmap加速內(nèi)核與用戶空間的消息傳遞。

對(duì)于select和poll函數(shù)的系統(tǒng)與內(nèi)核每次調(diào)用時(shí)的數(shù)據(jù)拷貝:epoll是通過(guò)內(nèi)核與用戶空間mmap同一塊內(nèi)存實(shí)現(xiàn)的,在epoll_ctl函數(shù)中:每次注冊(cè)新的事件到epoll句柄中時(shí)(在epoll_ctl中指定EPOLL_CTL_ADD),會(huì)把所有的fd拷貝進(jìn)內(nèi)核,而不是在epoll_wait的時(shí)候重復(fù)拷貝。epoll保證了每個(gè)fd在整個(gè)過(guò)程中只會(huì)拷貝一次。

3.調(diào)用后不需輪詢判斷描述符事件是否就緒。

對(duì)于select和poll函數(shù)每次調(diào)用后輪詢檢測(cè)事件是否發(fā)生:epoll的解決方案不像select或poll一樣每次都把current輪流加入fd對(duì)應(yīng)的設(shè)備等待隊(duì)列中,而只在epoll_ctl時(shí)把current掛一遍(這一遍必不可少)并為每個(gè)fd指定一個(gè)回調(diào)函數(shù),當(dāng)設(shè)備就緒,喚醒等待隊(duì)列上的等待者時(shí),就會(huì)調(diào)用這個(gè)回調(diào)函數(shù),而這個(gè)回調(diào)函數(shù)會(huì)把就緒的fd加入一個(gè)就緒鏈表)。epoll_wait的工作實(shí)際上就是在這個(gè)就緒鏈表中查看有沒(méi)有就緒的fd(利用schedule_timeout()實(shí)現(xiàn)睡一會(huì),判斷一會(huì)的效果)。

4.監(jiān)視描述符沒(méi)有個(gè)數(shù)上限。

epoll沒(méi)有這個(gè)限制,它所支持的FD上限是最大可以打開(kāi)文件的數(shù)目,這個(gè)數(shù)字一般遠(yuǎn)大于2048,注:在1GB內(nèi)存的機(jī)器上大約是10萬(wàn)左右,具體數(shù)目可以cat /proc/sys/fs/file-max察看,一般來(lái)說(shuō)這個(gè)數(shù)目和系統(tǒng)內(nèi)存關(guān)系很大。

5.IO效率不隨FD數(shù)目增加而線性下降。

傳統(tǒng)的select/poll另一個(gè)致命弱點(diǎn)就是當(dāng)你擁有一個(gè)很大的socket集合,不過(guò)由于網(wǎng)絡(luò)延時(shí),任一時(shí)間只有部分的socket是“活躍”的,但是select/poll每次調(diào)用都會(huì)線性掃描全部的集合,導(dǎo)致效率呈現(xiàn)線性下降。但是epoll不存在這個(gè)問(wèn)題,它只會(huì)對(duì)“活躍”的socket進(jìn)行操作---這是因?yàn)樵趦?nèi)核實(shí)現(xiàn)中epoll是根據(jù)每個(gè)fd上面的callback函數(shù)實(shí)現(xiàn)的。只有“活躍”的socket才會(huì)主動(dòng)的去調(diào)用 callback函數(shù),其他idle狀態(tài)socket則不會(huì)。

拓展:系統(tǒng)維護(hù)一顆紅黑樹(shù)(平衡搜索二叉樹(shù):穩(wěn)定)存儲(chǔ)監(jiān)視描述符,和一張鏈表存儲(chǔ)就緒的描述符。當(dāng)每次注冊(cè)或修改,刪除新的文件描述符到epoll句柄中時(shí),就會(huì)增加一個(gè)描述符到這課紅黑樹(shù)中(增刪改查簡(jiǎn)單),當(dāng)返回時(shí)檢測(cè)鏈表上是否有節(jié)點(diǎn),有節(jié)點(diǎn)則拷貝到用戶傳給它的那個(gè)描述符數(shù)組中。

epoll對(duì)于select和poll相比,顯著優(yōu)點(diǎn)是:

(1)select,poll實(shí)現(xiàn)需要自己不斷輪詢所有fd集合,直到設(shè)備就緒,期間可能要睡眠和喚醒多次交替。而epoll其實(shí)也需要調(diào)用epoll_wait不斷輪詢就緒鏈表,期間也可能多次睡眠和喚醒交替,但是它是設(shè)備就緒時(shí),調(diào)用回調(diào)函數(shù),把就緒fd放入就緒鏈表中,并喚醒在epoll_wait中進(jìn)入睡眠的進(jìn)程。雖然都要睡眠和交替,但是select和poll在“醒著”的時(shí)候要遍歷整個(gè)fd集合,而epoll在“醒著”的時(shí)候只要判斷一下就緒鏈表是否為空就行了,這節(jié)省了大量的CPU時(shí)間。這就是回調(diào)機(jī)制帶來(lái)的性能提升。

(2)select,poll每次調(diào)用都要把fd集合從用戶態(tài)往內(nèi)核態(tài)拷貝一次,并且要把current往設(shè)備等待隊(duì)列中掛一次,而epoll只要一次拷貝,而且把current往等待隊(duì)列上掛也只掛一次(在epoll_wait的開(kāi)始,注意這里的等待隊(duì)列并不是設(shè)備等待隊(duì)列,只是一個(gè)epoll內(nèi)部定義的等待隊(duì)列)。這也能節(jié)省不少的開(kāi)銷(xiāo)。

總結(jié):

select

  select能監(jiān)控的描述符個(gè)數(shù)由內(nèi)核中的FD_SETSIZE限制,僅為1024,這也是select最大的缺點(diǎn),因   為現(xiàn)在的服務(wù)器并發(fā)量遠(yuǎn)遠(yuǎn)不止1024。即使能重新編譯內(nèi)核改變FD_SETSIZE的值,但這并不能提高   select的性能。

  • 每次調(diào)用select都會(huì)線性掃描所有描述符的狀態(tài),在select結(jié)束后,用戶也要線性掃描fd_set數(shù)組才知道哪些描述符準(zhǔn)備就緒,等于說(shuō)每次調(diào)用復(fù)雜度都是O(n)的,在并發(fā)量大的情況下,每次掃描都是相當(dāng)耗時(shí)的,很有可能有未處理的連接等待超時(shí)。

  • 每次調(diào)用select都要在用戶空間和內(nèi)核空間里進(jìn)行內(nèi)存復(fù)制fd描述符等信息。

poll

  • poll使用pollfd結(jié)構(gòu)來(lái)存儲(chǔ)fd,突破了select中描述符數(shù)目的限制。

  • 與select的后兩點(diǎn)類(lèi)似,poll仍然需要將pollfd數(shù)組拷貝到內(nèi)核空間,之后依次掃描fd的狀態(tài),整體復(fù)雜度依然是O(n)的,在并發(fā)量大的情況下服務(wù)器性能會(huì)快速下降。

epoll

  • epoll維護(hù)的描述符數(shù)目不受到限制,而且性能不會(huì)隨著描述符數(shù)目的增加而下降。

  • 服務(wù)器的特點(diǎn)是經(jīng)常維護(hù)著大量連接,但其中某一時(shí)刻讀寫(xiě)的操作符數(shù)量卻不多。epoll先通過(guò)epoll_ctl注冊(cè)一個(gè)描述符到內(nèi)核中,并一直維護(hù)著而不像poll每次操作都將所有要監(jiān)控的描述符傳遞給內(nèi)核;在描述符讀寫(xiě)就緒時(shí),通過(guò)回掉函數(shù)將自己加入就緒隊(duì)列中,之后epoll_wait返回該就緒隊(duì)列。也就是說(shuō),epoll基本不做無(wú)用的操作,時(shí)間復(fù)雜度僅與活躍的客戶端數(shù)有關(guān),而不會(huì)隨著描述符數(shù)目的增加而下降。

  • epoll在傳遞內(nèi)核與用戶空間的消息時(shí)使用了內(nèi)存共享,而不是內(nèi)存拷貝,這也使得epoll的效率比poll和select更高。


三種多路復(fù)用IO實(shí)現(xiàn)方式:select,poll,epoll的區(qū)別

poll和epoll適用于關(guān)心描述符個(gè)數(shù)多的應(yīng)用程序。其中epoll對(duì)于每次只有很少描述符就緒很有優(yōu)勢(shì)(采用回調(diào)機(jī)制監(jiān)測(cè)描述符就緒)。

綜上:epoll是上面三個(gè)函數(shù)中效率最高的。


網(wǎng)頁(yè)名稱(chēng):三種多路復(fù)用IO實(shí)現(xiàn)方式:select,poll,epoll的區(qū)別
網(wǎng)頁(yè)地址:http://weahome.cn/article/ppjhsj.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部