現(xiàn)在有這么一個(gè)場(chǎng)景:我是一個(gè)很忙的大老板,我有100個(gè)手機(jī),手機(jī)來(lái)信息了,我的秘書就會(huì)告訴我“老板,你的手機(jī)來(lái)信息了?!蔽液苌鷼?,我的秘書就是這樣子,每次手機(jī)來(lái)信息就只告訴我來(lái)信息了,老板趕緊去看。但是她從來(lái)不把話說(shuō)清楚:到底是哪個(gè)手機(jī)來(lái)信息??!我可有100個(gè)手機(jī)??!于是,我只能一個(gè)一個(gè)手機(jī)去查看,來(lái)確定到底是哪幾個(gè)手機(jī)來(lái)信息了。這就是IO復(fù)用中select模型的缺點(diǎn)!老板心想,要是秘書能把來(lái)信息的手機(jī)直接拿到我桌子上就好了,那么我的效率肯定大增(這就是epoll模型)。
那我們先來(lái)總結(jié)一下select模型的缺點(diǎn):
單個(gè)進(jìn)程能夠監(jiān)視的文件描述符的數(shù)量存在最大限制,通常是1024,當(dāng)然可以更改數(shù)量,但由于select采用輪詢的方式掃描文件描述符,文件描述符數(shù)量越多,性能越差;(在linux內(nèi)核頭文件中,有這樣的定義:#define __FD_SETSIZE 1024)
內(nèi)核 / 用戶空間內(nèi)存拷貝問(wèn)題,select需要復(fù)制大量的句柄數(shù)據(jù)結(jié)構(gòu),產(chǎn)生巨大的開(kāi)銷;
select返回的是含有整個(gè)句柄的數(shù)組,應(yīng)用程序需要遍歷整個(gè)數(shù)組才能發(fā)現(xiàn)哪些句柄發(fā)生了事件;select的觸發(fā)方式是水平觸發(fā),應(yīng)用程序如果沒(méi)有完成對(duì)一個(gè)已經(jīng)就緒的文件描述符進(jìn)行IO操作,那么之后每次select調(diào)用還是會(huì)將這些文件描述符通知進(jìn)程。
設(shè)想一下如下場(chǎng)景:有100萬(wàn)個(gè)客戶端同時(shí)與一個(gè)服務(wù)器進(jìn)程保持著TCP連接。而每一時(shí)刻,通常只有幾百上千個(gè)TCP連接是活躍的(事實(shí)上大部分場(chǎng)景都是這種情況)。如何實(shí)現(xiàn)這樣的高并發(fā)?
粗略計(jì)算一下,一個(gè)進(jìn)程最多有1024個(gè)文件描述符,那么我們需要開(kāi)1000個(gè)進(jìn)程來(lái)處理100萬(wàn)個(gè)客戶連接。如果我們使用select模型,這1000個(gè)進(jìn)程里某一段時(shí)間內(nèi)只有數(shù)個(gè)客戶連接需要數(shù)據(jù)的接收,那么我們就不得不輪詢1024個(gè)文件描述符以確定究竟是哪個(gè)客戶有數(shù)據(jù)可讀,想想如果1000個(gè)進(jìn)程都有類似的行為,那系統(tǒng)資源消耗可有多大??!
針對(duì)select模型的缺點(diǎn),epoll模型被提出來(lái)了!