• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            chaosuper85

            C++博客 首頁(yè) 新隨筆 聯(lián)系 聚合 管理
              118 Posts :: 0 Stories :: 3 Comments :: 0 Trackbacks
            select, poll和epoll的區(qū)別(轉(zhuǎn)載) select()系統(tǒng)調(diào)用提供一個(gè)機(jī)制來(lái)實(shí)現(xiàn)同步多元I/O: #include #include #include int select (int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); FD_CLR(int fd, fd_set *set); FD_ISSET(int fd, fd_set *set); FD_SET(int fd, fd_set *set); FD_ZERO(fd_set *set); 調(diào)用select()將阻塞,直到指定的文件描述符準(zhǔn)備好執(zhí)行I/O,或者可選參數(shù)timeout指定的時(shí)間已經(jīng)過(guò)去。 監(jiān)視的文件描述符分為三類(lèi)set,每一種對(duì)應(yīng)等待不同的事件。readfds中列出的文件描述符被監(jiān)視是否有數(shù)據(jù)可供讀取(如果讀取操作完成則不會(huì)阻塞)。writefds中列出的文件描述符則被監(jiān)視是否寫(xiě)入操作完成而不阻塞。最后,exceptfds中列出的文件描述符則被監(jiān)視是否發(fā)生異常,或者無(wú)法控制的數(shù)據(jù)是否可用(這些狀態(tài)僅僅應(yīng)用于套接字)。這三類(lèi)set可以是NULL,這種情況下select()不監(jiān)視這一類(lèi)事件。 select()成功返回時(shí),每組set都被修改以使它只包含準(zhǔn)備好I/O的文件描述符。例如,假設(shè)有兩個(gè)文件描述符,值分別是7和9,被放在readfds中。當(dāng)select()返回時(shí),如果7仍然在set中,則這個(gè)文件描述符已經(jīng)準(zhǔn)備好被讀取而不會(huì)阻塞。如果9已經(jīng)不在set中,則讀取它將可能會(huì)阻塞(我說(shuō)可能是因?yàn)閿?shù)據(jù)可能正好在select返回后就可用,這種情況下,下一次調(diào)用select()將返回文件描述符準(zhǔn)備好讀取)。 第一個(gè)參數(shù)n,等于所有set中最大的那個(gè)文件描述符的值加1。因此,select()的調(diào)用者負(fù)責(zé)檢查哪個(gè)文件描述符擁有最大值,并且把這個(gè)值加1再傳遞給第一個(gè)參數(shù)。 timeout參數(shù)是一個(gè)指向timeval結(jié)構(gòu)體的指針,timeval定義如下: #include struct timeval { long tv_sec; /* seconds */ long tv_usec; /* 10E-6 second */ }; 如果這個(gè)參數(shù)不是NULL,則即使沒(méi)有文件描述符準(zhǔn)備好I/O,select()也會(huì)在經(jīng)過(guò)tv_sec秒和tv_usec微秒后返回。當(dāng)select()返回時(shí),timeout參數(shù)的狀態(tài)在不同的系統(tǒng)中是未定義的,因此每次調(diào)用select()之前必須重新初始化timeout和文件描述符set。實(shí)際上,當(dāng)前版本的Linux會(huì)自動(dòng)修改timeout參數(shù),設(shè)置它的值為剩余時(shí)間。因此,如果timeout被設(shè)置為5秒,然后在文件描述符準(zhǔn)備好之前經(jīng)過(guò)了3秒,則這一次調(diào)用select()返回時(shí)tv_sec將變?yōu)?。 如果timeout中的兩個(gè)值都設(shè)置為0,則調(diào)用select()將立即返回,報(bào)告調(diào)用時(shí)所有未決的事件,但不等待任何隨后的事件。 文件描述符set不會(huì)直接操作,一般使用幾個(gè)助手宏來(lái)管理。這允許Unix系統(tǒng)以自己喜歡的方式來(lái)實(shí)現(xiàn)文件描述符set。但大多數(shù)系統(tǒng)都簡(jiǎn)單地實(shí)現(xiàn)set為位數(shù)組。FD_ZERO移除指定set中的所有文件描述符。每一次調(diào)用select()之前都應(yīng)該先調(diào)用它。 fd_set writefds; FD_ZERO(&writefds); FD_SET添加一個(gè)文件描述符到指定的set中,F(xiàn)D_CLR則從指定的set中移除一個(gè)文件描述符: FD_SET(fd, &writefds); /* add 'fd' to the set */ FD_CLR(fd, &writefds); /* oops, remove 'fd' from the set */ 設(shè)計(jì)良好的代碼應(yīng)該永遠(yuǎn)不使用FD_CLR,而且實(shí)際情況中它也確實(shí)很少被使用。 FD_ISSET測(cè)試一個(gè)文件描述符是否指定set的一部分。如果文件描述符在set中則返回一個(gè)非0整數(shù),不在則返回0。FD_ISSET在調(diào)用select()返回之后使用,測(cè)試指定的文件描述符是否準(zhǔn)備好相關(guān)動(dòng)作: if (FD_ISSET(fd, &readfds)) /* 'fd' is readable without blocking! */ 因?yàn)槲募枋龇鹲et是靜態(tài)創(chuàng)建的,它們對(duì)文件描述符的最大數(shù)目強(qiáng)加了一個(gè)限制,能夠放進(jìn)set中的最大文件描述符的值由FD_SETSIZE指定。在Linux中,這個(gè)值是1024。本章后面我們還將看到這個(gè)限制的衍生物。 返回值和錯(cuò)誤代碼 select()成功時(shí)返回準(zhǔn)備好I/O的文件描述符數(shù)目,包括所有三個(gè)set。如果提供了timeout,返回值可能是0;錯(cuò)誤時(shí)返回-1,并且設(shè)置errno為下面幾個(gè)值之一: EBADF 給某個(gè)set提供了無(wú)效文件描述符。 EINTR 等待時(shí)捕獲到信號(hào),可以重新發(fā)起調(diào)用。 EINVAL 參數(shù)n為負(fù)數(shù),或者指定的timeout非法。 ENOMEM 不夠可用內(nèi)存來(lái)完成請(qǐng)求。 -------------------------------------------------------------------------------------------------------------- poll()系統(tǒng)調(diào)用是System V的多元I/O解決方案。它解決了select()的幾個(gè)不足,盡管select()仍然經(jīng)常使用(多數(shù)還是出于習(xí)慣,或者打著可移植的名義): #include int poll (struct pollfd *fds, unsigned int nfds, int timeout); 和select()不一樣,poll()沒(méi)有使用低效的三個(gè)基于位的文件描述符set,而是采用了一個(gè)單獨(dú)的結(jié)構(gòu)體pollfd數(shù)組,由fds指針指向這個(gè)組。pollfd結(jié)構(gòu)體定義如下: #include struct pollfd { int fd; /* file descriptor */ short events; /* requested events to watch */ short revents; /* returned events witnessed */ }; 每一個(gè)pollfd結(jié)構(gòu)體指定了一個(gè)被監(jiān)視的文件描述符,可以傳遞多個(gè)結(jié)構(gòu)體,指示poll()監(jiān)視多個(gè)文件描述符。每個(gè)結(jié)構(gòu)體的events域是監(jiān)視該文件描述符的事件掩碼,由用戶(hù)來(lái)設(shè)置這個(gè)域。revents域是文件描述符的操作結(jié)果事件掩碼。內(nèi)核在調(diào)用返回時(shí)設(shè)置這個(gè)域。events域中請(qǐng)求的任何事件都可能在revents域中返回。合法的事件如下: POLLIN 有數(shù)據(jù)可讀。 POLLRDNORM 有普通數(shù)據(jù)可讀。 POLLRDBAND 有優(yōu)先數(shù)據(jù)可讀。 POLLPRI 有緊迫數(shù)據(jù)可讀。 POLLOUT 寫(xiě)數(shù)據(jù)不會(huì)導(dǎo)致阻塞。 POLLWRNORM 寫(xiě)普通數(shù)據(jù)不會(huì)導(dǎo)致阻塞。 POLLWRBAND 寫(xiě)優(yōu)先數(shù)據(jù)不會(huì)導(dǎo)致阻塞。 POLLMSG SIGPOLL消息可用。 此外,revents域中還可能返回下列事件: POLLER 指定的文件描述符發(fā)生錯(cuò)誤。 POLLHUP 指定的文件描述符掛起事件。 POLLNVAL 指定的文件描述符非法。 這些事件在events域中無(wú)意義,因?yàn)樗鼈冊(cè)诤线m的時(shí)候總是會(huì)從revents中返回。使用poll()和select()不一樣,你不需要顯式地請(qǐng)求異常情況報(bào)告。 POLLIN | POLLPRI等價(jià)于select()的讀事件,POLLOUT | POLLWRBAND等價(jià)于select()的寫(xiě)事件。POLLIN等價(jià)于POLLRDNORM | POLLRDBAND,而POLLOUT則等價(jià)于POLLWRNORM。 例如,要同時(shí)監(jiān)視一個(gè)文件描述符是否可讀和可寫(xiě),我們可以設(shè)置events為POLLIN | POLLOUT。在poll返回時(shí),我們可以檢查revents中的標(biāo)志,對(duì)應(yīng)于文件描述符請(qǐng)求的events結(jié)構(gòu)體。如果POLLIN事件被設(shè)置,則文件描述符可以被讀取而不阻塞。如果POLLOUT被設(shè)置,則文件描述符可以寫(xiě)入而不導(dǎo)致阻塞。這些標(biāo)志并不是互斥的:它們可能被同時(shí)設(shè)置,表示這個(gè)文件描述符的讀取和寫(xiě)入操作都會(huì)正常返回而不阻塞。 timeout參數(shù)指定等待的毫秒數(shù),無(wú)論I/O是否準(zhǔn)備好,poll都會(huì)返回。timeout指定為負(fù)數(shù)值表示無(wú)限超時(shí);timeout為0指示poll調(diào)用立即返回并列出準(zhǔn)備好I/O的文件描述符,但并不等待其它的事件。這種情況下,poll()就像它的名字那樣,一旦選舉出來(lái),立即返回。 返回值和錯(cuò)誤代碼 成功時(shí),poll()返回結(jié)構(gòu)體中revents域不為0的文件描述符個(gè)數(shù);如果在超時(shí)前沒(méi)有任何事件發(fā)生,poll()返回0;失敗時(shí),poll()返回-1,并設(shè)置errno為下列值之一: EBADF 一個(gè)或多個(gè)結(jié)構(gòu)體中指定的文件描述符無(wú)效。 EFAULT fds指針指向的地址超出進(jìn)程的地址空間。 EINTR 請(qǐng)求的事件之前產(chǎn)生一個(gè)信號(hào),調(diào)用可以重新發(fā)起。 EINVAL nfds參數(shù)超出PLIMIT_NOFILE值。 ENOMEM 可用內(nèi)存不足,無(wú)法完成請(qǐng)求。 -------------------------------------------------------------------------------------------------------------- 以上內(nèi)容來(lái)自《OReilly.Linux.System.Programming - Talking.Directly.to.the.Kernel.and.C.Library.2007》 -------------------------------------------------------------------------------------------------------------- epoll的優(yōu)點(diǎn): 1.支持一個(gè)進(jìn)程打開(kāi)大數(shù)目的socket描述符(FD) select 最不能忍受的是一個(gè)進(jìn)程所打開(kāi)的FD是有一定限制的,由FD_SETSIZE設(shè)置,默認(rèn)值是2048。對(duì)于那些需要支持的上萬(wàn)連接數(shù)目的IM服務(wù)器來(lái)說(shuō)顯然太少了。這時(shí)候你一是可以選擇修改這個(gè)宏然后重新編譯內(nèi)核,不過(guò)資料也同時(shí)指出這樣會(huì)帶來(lái)網(wǎng)絡(luò)效率的下降,二是可以選擇多進(jìn)程的解決方案(傳統(tǒng)的 Apache方案),不過(guò)雖然linux上面創(chuàng)建進(jìn)程的代價(jià)比較小,但仍舊是不可忽視的,加上進(jìn)程間數(shù)據(jù)同步遠(yuǎn)比不上線程間同步的高效,所以也不是一種完美的方案。不過(guò) epoll則沒(méi)有這個(gè)限制,它所支持的FD上限是最大可以打開(kāi)文件的數(shù)目,這個(gè)數(shù)字一般遠(yuǎn)大于2048,舉個(gè)例子,在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)系很大。 2.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ì),在這點(diǎn)上,epoll實(shí)現(xiàn)了一個(gè)"偽"AIO,因?yàn)檫@時(shí)候推動(dòng)力在os內(nèi)核。在一些 benchmark中,如果所有的socket基本上都是活躍的---比如一個(gè)高速LAN環(huán)境,epoll并不比select/poll有什么效率,相反,如果過(guò)多使用epoll_ctl,效率相比還有稍微的下降。但是一旦使用idle connections模擬WAN環(huán)境,epoll的效率就遠(yuǎn)在select/poll之上了。 3.使用mmap加速內(nèi)核與用戶(hù)空間的消息傳遞。 這點(diǎn)實(shí)際上涉及到epoll的具體實(shí)現(xiàn)了。無(wú)論是select,poll還是epoll都需要內(nèi)核把FD消息通知給用戶(hù)空間,如何避免不必要的內(nèi)存拷貝就很重要,在這點(diǎn)上,epoll是通過(guò)內(nèi)核于用戶(hù)空間mmap同一塊內(nèi)存實(shí)現(xiàn)的。而如果你想我一樣從2.5內(nèi)核就關(guān)注epoll的話,一定不會(huì)忘記手工 mmap這一步的。 4.內(nèi)核微調(diào) 這一點(diǎn)其實(shí)不算epoll的優(yōu)點(diǎn)了,而是整個(gè)linux平臺(tái)的優(yōu)點(diǎn)。也許你可以懷疑linux平臺(tái),但是你無(wú)法回避linux平臺(tái)賦予你微調(diào)內(nèi)核的能力。比如,內(nèi)核TCP/IP協(xié)議棧使用內(nèi)存池管理sk_buff結(jié)構(gòu),那么可以在運(yùn)行時(shí)期動(dòng)態(tài)調(diào)整這個(gè)內(nèi)存pool(skb_head_pool)的大小--- 通過(guò)echo XXXX>/proc/sys/net/core/hot_list_length完成。再比如listen函數(shù)的第2個(gè)參數(shù)(TCP完成3次握手的數(shù)據(jù)包隊(duì)列長(zhǎng)度),也可以根據(jù)你平臺(tái)內(nèi)存大小動(dòng)態(tài)調(diào)整。更甚至在一個(gè)數(shù)據(jù)包面數(shù)目巨大但同時(shí)每個(gè)數(shù)據(jù)包本身大小卻很小的特殊系統(tǒng)上嘗試最新的NAPI網(wǎng)卡驅(qū)動(dòng)架構(gòu)。
            posted on 2010-02-03 22:45 chaosuper 閱讀(251) 評(píng)論(0)  編輯 收藏 引用

            只有注冊(cè)用戶(hù)登錄后才能發(fā)表評(píng)論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問(wèn)   Chat2DB   管理


            精品久久久久久国产潘金莲 | 久久久久久伊人高潮影院| 热综合一本伊人久久精品| 亚洲精品成人网久久久久久| 久久精品国产99国产精品亚洲 | 久久久久国产| 亚洲精品乱码久久久久久蜜桃不卡 | 久久精品亚洲一区二区三区浴池| 国产精品久久波多野结衣| 精品国产综合区久久久久久 | 久久国产综合精品五月天| 久久亚洲中文字幕精品一区| 久久精品国产91久久麻豆自制| 一本色道久久综合狠狠躁篇| 国产午夜精品理论片久久影视 | 国产一区二区三区久久精品| 伊人久久国产免费观看视频| 曰曰摸天天摸人人看久久久| 伊人久久大香线蕉亚洲| 日本精品久久久久影院日本| 久久九九全国免费| 久久不见久久见免费视频7| 亚洲精品高清一二区久久| 老司机国内精品久久久久| 亚洲中文字幕无码久久2017| 三级韩国一区久久二区综合| 国产成人久久777777| 久久九九亚洲精品| 久久久免费精品re6| 久久免费看黄a级毛片| 亚洲欧美一区二区三区久久| 人妻无码久久精品| 香港aa三级久久三级老师2021国产三级精品三级在| 色婷婷综合久久久久中文 | 亚洲国产精品一区二区久久| 亚洲va中文字幕无码久久不卡| 亚洲欧洲久久久精品| 无码任你躁久久久久久久| 四虎国产精品成人免费久久| 久久久综合香蕉尹人综合网| 色婷婷久久久SWAG精品|