網(wǎng)絡(luò)IO的發(fā)展過(guò)程隨著Linux內(nèi)核版本的迭代而發(fā)生變化,主要經(jīng)歷以下幾個(gè)階段:
1. 阻塞 IO(BIO)
2. 非阻塞 IO(NIO)
3. IO 多路復(fù)用第一版(select/poll)
4. IO 多路復(fù)用第二版(epoll)
5. 異步 IO(AIO)
而每一個(gè)階段,都是因?yàn)楫?dāng)前的網(wǎng)絡(luò)有一些缺陷,因此又在不斷改進(jìn)該缺陷。這是網(wǎng)絡(luò) IO 一直演變過(guò)程中的本質(zhì)。
1.網(wǎng)絡(luò)的兩個(gè)階段
在網(wǎng)絡(luò)中,我們通??梢詫⑵鋸V義上劃分為以下兩個(gè)階段:
第一階段:硬件接口到內(nèi)核態(tài)
第二階段:內(nèi)核態(tài)到用戶態(tài)
我們上網(wǎng)發(fā)送信息時(shí),大部分?jǐn)?shù)據(jù)都是通過(guò)網(wǎng)線傳遞的。因此對(duì)于兩臺(tái)計(jì)算機(jī)而言,要進(jìn)行網(wǎng)絡(luò)通信,其數(shù)據(jù)都是先從應(yīng)用程序傳遞到傳輸層(TCP/UDP)到達(dá)內(nèi)核態(tài),然后再到網(wǎng)絡(luò)層、數(shù)據(jù)鏈路層、物理層,接著數(shù)據(jù)傳遞到硬件網(wǎng)卡。接收信息時(shí),通過(guò)網(wǎng)絡(luò)傳輸介質(zhì)傳遞到對(duì)端機(jī)器的網(wǎng)卡,然后再一步一步數(shù)據(jù)從網(wǎng)卡傳遞到內(nèi)核態(tài),最后再拷貝到用戶態(tài)。
上面所說(shuō)的兩個(gè)階段是接收信息時(shí)的兩個(gè)階段。
2.概念區(qū)別
2.1 阻塞 IO 和非阻塞 IO 的區(qū)別
阻塞IO與非阻塞IO主要區(qū)別是在第一階段,硬件接口到內(nèi)核態(tài)是否發(fā)生阻塞。
如果用戶發(fā)起了讀寫請(qǐng)求,但內(nèi)核態(tài)數(shù)據(jù)還未準(zhǔn)備就緒,該階段不會(huì)阻塞用戶操作,內(nèi)核立馬返回,則稱為非阻塞 IO。
如果該階段一直阻塞用戶操作。直到內(nèi)核態(tài)數(shù)據(jù)準(zhǔn)備就緒,才返回。這種方式稱為阻塞 IO。
2.2 同步 IO 和異步 IO 的區(qū)別
數(shù)據(jù)傳輸有兩個(gè)階段,在此處只要任何一個(gè)階段會(huì)阻塞用戶請(qǐng)求,都將其稱為同步 IO,兩個(gè)階段都不阻塞,則稱為異步 IO。
在目前所有的操作系統(tǒng)中,linux 中的 epoll、mac 的 kqueue 都屬于同步 IO,因?yàn)槠湓诘诙A段(數(shù)據(jù)從內(nèi)核態(tài)到用戶態(tài))都會(huì)發(fā)生拷貝阻塞。而只有 windows 中的 IOCP 才真正屬于異步 IO,即 AIO。
3. 阻塞 IO
根據(jù)前面的介紹,阻塞 IO 主要指的是第一階段(硬件網(wǎng)卡到內(nèi)核態(tài))。
3.1 阻塞 IO的概念
阻塞 IO,顧名思義當(dāng)用戶發(fā)生了系統(tǒng)調(diào)用后,如果數(shù)據(jù)未從網(wǎng)卡到達(dá)內(nèi)核態(tài),內(nèi)核態(tài)數(shù)據(jù)未準(zhǔn)備好,此時(shí)會(huì)一直阻塞。直到數(shù)據(jù)就緒,然后從內(nèi)核態(tài)拷貝到用戶態(tài)再返回。
3.2 阻塞 IO 的缺點(diǎn)
在一般使用阻塞 IO 時(shí),都需要配置多線程來(lái)使用,最常見(jiàn)的模型是阻塞 IO+多線程,每個(gè)連接一個(gè)單獨(dú)的線程進(jìn)行處理。
我們知道,一般一個(gè)程序可以開(kāi)辟的線程是有限的,而且開(kāi)辟線程的開(kāi)銷也是比較大的。也正是這種方式,會(huì)導(dǎo)致一個(gè)應(yīng)用程序可以處理的客戶端請(qǐng)求受限。面對(duì)百萬(wàn)連接的情況,是無(wú)法處理。
4. 非阻塞 IO
非阻塞 IO 是為了解決前面提到的阻塞 IO 的缺陷而引出的。
4.1 非阻塞 IO 的概念
非阻塞 IO:見(jiàn)名知意,就是在第一階段(網(wǎng)卡-內(nèi)核態(tài))數(shù)據(jù)未到達(dá)時(shí)不等待,然后直接返回。因此非阻塞 IO 需要不斷的用戶發(fā)起請(qǐng)求,詢問(wèn)內(nèi)核數(shù)據(jù)好了沒(méi),好了沒(méi)。非阻塞 IO 是需要系統(tǒng)內(nèi)核支持的。
4.2 非阻塞 IO 的優(yōu)點(diǎn)
非阻塞 IO 解決了阻塞 IO每個(gè)連接一個(gè)線程處理的問(wèn)題,所以其最大的優(yōu)點(diǎn)就是 一個(gè)線程可以處理多個(gè)連接,這也是其非阻塞決定的。
4.3 非阻塞 IO 的缺點(diǎn)
但這種模式,也有一個(gè)問(wèn)題,就是需要用戶多次發(fā)起系統(tǒng)調(diào)用。頻繁的系統(tǒng)調(diào)用是比較消耗系統(tǒng)資源的。
因此,既然存在這樣的問(wèn)題,那么自然而然我們就需要解決該問(wèn)題:保留非阻塞 IO 的優(yōu)點(diǎn)的前提下,減少系統(tǒng)調(diào)用。
5. IO 多路復(fù)用第一版
為了解決非阻塞 IO 存在的頻繁的系統(tǒng)調(diào)用這個(gè)問(wèn)題,隨著內(nèi)核的發(fā)展,出現(xiàn)了 IO 多路復(fù)用模型。
5.1 IO 多路復(fù)用概念
多路復(fù)用主要復(fù)用的是通過(guò)有限次的系統(tǒng)調(diào)用來(lái)實(shí)現(xiàn)管理多個(gè)網(wǎng)絡(luò)連接。最簡(jiǎn)單來(lái)說(shuō),我目前有 10 個(gè)連接,我可以通過(guò)一次系統(tǒng)調(diào)用將這 10 個(gè)連接都丟給內(nèi)核,讓內(nèi)核告訴我,哪些連接上面數(shù)據(jù)準(zhǔn)備好了,然后我再去讀取每個(gè)就緒的連接上的數(shù)據(jù)。因此,IO 多路復(fù)用,復(fù)用的是系統(tǒng)調(diào)用。通過(guò)有限次系統(tǒng)調(diào)用判斷海量連接是否數(shù)據(jù)準(zhǔn)備好了。
5.2 IO 多路復(fù)用第一版的概念
使用select/poll做多路復(fù)用。
5.3 IO 多路復(fù)用第一版的優(yōu)點(diǎn)
IO 多路復(fù)用,主要在于復(fù)用,通過(guò) select()或者 poll()將多個(gè) socket fds 批量通過(guò)系統(tǒng)調(diào)用傳遞給內(nèi)核,由內(nèi)核進(jìn)行循環(huán)遍歷判斷哪些 fd 上數(shù)據(jù)就緒了,然后將就緒的 readyfds 返回給用戶。再由用戶進(jìn)行挨個(gè)遍歷就緒好的 fd,讀取或者寫入數(shù)據(jù)。
所以通過(guò) IO 多路復(fù)用+非阻塞 IO,一方面降低了系統(tǒng)調(diào)用次數(shù),另一方面可以用極少的線程來(lái)處理多個(gè)網(wǎng)絡(luò)連接。
5.4 IO 多路復(fù)用第一版的缺點(diǎn)
雖然第一版 IO 多路復(fù)用解決了之前提到的頻繁的系統(tǒng)調(diào)用次數(shù),但同時(shí)引入了新的問(wèn)題:用戶需要每次將海量的 socket fds 集合從用戶態(tài)傳遞到內(nèi)核態(tài),讓內(nèi)核態(tài)去檢測(cè)哪些網(wǎng)絡(luò)連接數(shù)據(jù)就緒了
但這個(gè)地方會(huì)出現(xiàn)頻繁的將海量 fd 集合從用戶態(tài)傳遞到內(nèi)核態(tài),再?gòu)膬?nèi)核態(tài)拷貝到用戶態(tài)。所以,這個(gè)地方開(kāi)銷也挺大。
既然還有這個(gè)問(wèn)題,那我們繼續(xù)開(kāi)始解決這個(gè)問(wèn)題,因此就引出了第二版的 IO 多路復(fù)用。
其實(shí)思路也挺簡(jiǎn)單,既然需要拷貝,那就想辦法,不拷貝。既然不拷貝,那就在內(nèi)核開(kāi)辟一段區(qū)域咯
6. IO 多路復(fù)用第二版
epoll 的出現(xiàn)是為了解決前面提到的 IO 多路復(fù)用第一版的問(wèn)題。
6.1 IO 多路復(fù)用第二版的優(yōu)點(diǎn)
IO 多路復(fù)用第二版 epoll 的優(yōu)點(diǎn)在于:
一開(kāi)始就在內(nèi)核態(tài)分配了一段空間,來(lái)存放管理的 fd,所以在每次連接建立后,交給 epoll 管理時(shí),需要將其添加到原先分配的空間中,后面再管理時(shí)就不需要頻繁的從用戶態(tài)拷貝管理的 fd 集合。通過(guò)這種方式大大的提升了性能。
所以現(xiàn)在的 IO 多路復(fù)用主要指 epoll。
7. 異步 IO
前面介紹的所有網(wǎng)絡(luò) IO 都是同步 IO,因?yàn)楫?dāng)數(shù)據(jù)在內(nèi)核態(tài)就緒時(shí),在內(nèi)核態(tài)拷貝用用戶態(tài)的過(guò)程中,仍然會(huì)有短暫時(shí)間的阻塞等待。而異步 IO 指:內(nèi)核態(tài)拷貝數(shù)據(jù)到用戶態(tài)這種方式也是交給系統(tǒng)線程來(lái)實(shí)現(xiàn),不由用戶線程完成,目前只有 windows 系統(tǒng)的 IOCP 是屬于異步 IO。