計算機(jī)底層原理-網(wǎng)絡(luò)IO模型的分析

Linux的網(wǎng)絡(luò)IO模型

網(wǎng)絡(luò)IO的本質(zhì)是socket的讀寫,socket在Linux中被抽象為流,IO可以理解為對流的操作。

IO的分類和范疇

IO本身可以分為內(nèi)存IO、網(wǎng)絡(luò)IO和磁盤IO還有緩存IO等,一般討論IO時更多是指后(網(wǎng)絡(luò)IO和磁盤IO,因為這兩個是最慢的哈哈),此處特別分析和說明網(wǎng)絡(luò)IO。

操作處理的分類

阻塞/非阻塞

針對函數(shù)/方法的實現(xiàn)方式而言,即數(shù)據(jù)就緒之前是立刻返回還是等待,即發(fā)起IO請求后是否會阻塞。

阻塞IO機(jī)制

阻塞IO情況下,當(dāng)用戶調(diào)用read后,用戶線程會被阻塞,等內(nèi)核數(shù)據(jù)準(zhǔn)備好并且數(shù)據(jù)從內(nèi)核緩沖區(qū)拷貝到用戶態(tài)緩存區(qū)后read才會返回。可以看到是阻塞的兩個部分。

  • CPU把數(shù)據(jù)從磁盤讀到內(nèi)核緩沖區(qū)。

  • CPU把數(shù)據(jù)從內(nèi)核緩沖區(qū)拷貝到用戶緩沖區(qū)。

image

非阻塞IO機(jī)制

image
  • 非阻塞IO發(fā)出read請求后發(fā)現(xiàn)數(shù)據(jù)沒準(zhǔn)備好,會繼續(xù)往下執(zhí)行,此時應(yīng)用程序會不斷輪詢polling內(nèi)核詢問數(shù)據(jù)是否準(zhǔn)備好,當(dāng)數(shù)據(jù)沒有準(zhǔn)備好時,內(nèi)核立即返回EWOULDBLOCK錯誤。

  • 直到數(shù)據(jù)被拷貝到應(yīng)用程序緩沖區(qū),read請求才獲取到結(jié)果。并且你要注意!這里最后一次 read 調(diào)用獲取數(shù)據(jù)的過程,是一個同步的過程,是需要等待的過程。這里的同步指的是內(nèi)核態(tài)的數(shù)據(jù)拷貝到用戶程序的緩存區(qū)這個過程。

同步/異步

IO讀操作指數(shù)據(jù)流經(jīng):網(wǎng)絡(luò) -> 內(nèi)核緩沖區(qū) -> 用戶內(nèi)存

  • 同步和異步的主要區(qū)別在于數(shù)據(jù)從內(nèi)核緩沖區(qū) -> 用戶內(nèi)存這個過程需不需要用戶進(jìn)程等待。

  • 等待內(nèi)核態(tài)準(zhǔn)備數(shù)據(jù)結(jié)束之后,會自動回通知用戶態(tài)的線程進(jìn)行讀取信息數(shù)據(jù),此時之前用戶態(tài)的線程不需要等待,可以去做其他操作。

對于一個網(wǎng)絡(luò)IO,會涉及到兩個系統(tǒng)對象,一個是調(diào)用這個IO的process(or thread)【用戶態(tài)】,另一個就是系統(tǒng)內(nèi)核(kernel)【內(nèi)核態(tài)】

當(dāng)一個用戶態(tài)發(fā)生read操作發(fā)生時,它會經(jīng)歷兩個階段:

  • 第一階段:用戶態(tài)線程等待內(nèi)核態(tài)的數(shù)據(jù)準(zhǔn)備 (Waiting for the data to be ready)。

  • 第二階段:用戶態(tài)線程,將數(shù)據(jù)從內(nèi)核拷貝到進(jìn)程中 (Copying the data from the kernel to the process)。

    • 第一步:通常涉及等待網(wǎng)絡(luò)上的數(shù)據(jù)分組到達(dá),然后被復(fù)制到內(nèi)核的某個緩沖區(qū)。

    • 第二步:把數(shù)據(jù)從內(nèi)核緩沖區(qū)復(fù)制到(用戶態(tài))應(yīng)用進(jìn)程緩沖區(qū)。網(wǎng)絡(luò)應(yīng)用處理的是兩大類問題:網(wǎng)絡(luò)IO、數(shù)據(jù)計算。前者給應(yīng)用帶來的性能瓶頸更大。

網(wǎng)絡(luò)IO的模型大致有如下幾種:

  • 同步模型(synchronous IO)
  • 阻塞IO模型(blocking IO)
  • 非阻塞IO模型(non-blocking IO)
  • 多路復(fù)用IO模型(multiplexing IO)
  • 信號驅(qū)動IO模型(signal-driven IO)
  • 異步IO(asynchronous IO)

阻塞IO模型(blocking IO)

Linux中,默認(rèn)情況下所有的socket都是blocking,一個典型的讀操作流程如下:

image

當(dāng)用戶進(jìn)程調(diào)用了recvfrom這個系統(tǒng)調(diào)用,如上所述,會有兩個階段

準(zhǔn)備數(shù)據(jù):

  • 很多時候數(shù)據(jù)在一開始還沒有到達(dá),這個時候kernel就要等待足夠的數(shù)據(jù)到來。而用戶進(jìn)程會一直阻塞。

  • 當(dāng)kernel等到數(shù)據(jù)準(zhǔn)備好了,它會將數(shù)據(jù)從kernel中拷貝到用戶內(nèi)存,然后kernel返回,用戶進(jìn)程結(jié)束block狀態(tài),重新運行。

Blocking IO的特點就是IO執(zhí)行的兩個階段都是block了的。

非阻塞IO模型(non-blocking IO)[poll]

在Linux中,可以通過設(shè)置socket使其變?yōu)閚on-blocking,其流程如下:

image
  • 當(dāng)用戶進(jìn)程調(diào)用了recvfrom這個系統(tǒng)調(diào)用,如果kernel中的數(shù)據(jù)還沒有準(zhǔn)備好,那么用戶進(jìn)程不會block而是立刻返回一個error,即從用戶的角度而言,不需要等待,馬上得到一個結(jié)果。

  • 從圖中可以看出,用戶進(jìn)程在判斷結(jié)果是一個error后,了解到數(shù)據(jù)還沒有準(zhǔn)備好,于是就不斷重復(fù)上述操作直至kernel中的數(shù)據(jù)準(zhǔn)備好,然后它馬上將數(shù)據(jù)拷貝到了用戶內(nèi)存,然后返回。

多路復(fù)用IO模型(multiplexing IO)

select/epoll/evpoll,也被稱作是Event-Driven IO。好處是單個process可以同時處理多個網(wǎng)絡(luò)連接的IO。

  • 基本原理可見下面的“IO復(fù)用技術(shù)”。也叫多路IO就緒通知。

  • 這是一種進(jìn)程預(yù)先告知內(nèi)核的能力,讓內(nèi)核發(fā)現(xiàn)進(jìn)程指定的一個或多個IO條件就緒了,就通知用戶進(jìn)程

  • 使得一個進(jìn)程能在一連串的事件上等待。

image
  • 這個流程和Blocking IO的流程其實并沒有太多不同,事實上僅從圖中看起來,由于需要進(jìn)行兩次系統(tǒng)調(diào)用,可能更差一些。但是,Select的優(yōu)勢在于它可以同時處理多個連接。

  • 如果處理的連接數(shù)不是很高的話,使用“Select/Epoll 的 Web Server”不一定比使用“多線程 + BIO的Web Server”性能更好,反而延遲會更大。

Select/Epoll的優(yōu)勢并不是對于單個連接能處理得更快,而是在于能處理更多的連接。

在IO多路復(fù)用模型中,實際中,對于每一個socket,一般都設(shè)置成為non-blocking,但是,如上圖所示,整個用戶的process其實是一直被block的。只不過process是被select這個函數(shù)block,而不是被socket IO給block。

異步IO(asynchronous IO)

用戶進(jìn)程發(fā)起read操作之后,立刻就可以開始去做其它的事。

image
  1. 從kernel的角度,當(dāng)它受到一個asynchronous read之后,首先它會立刻返回,所以不會對用戶進(jìn)程產(chǎn)生任何block。

  2. kernel會等待數(shù)據(jù)準(zhǔn)備完成,然后將數(shù)據(jù)拷貝到用戶內(nèi)存,當(dāng)這一切都完成之后,kernel會給用戶進(jìn)程發(fā)送一個signal,告訴它read操作完成了。

比較

image

非阻塞和異步的區(qū)別

經(jīng)過上面的介紹,會發(fā)現(xiàn)non-blocking IO和asynchronous IO的區(qū)別還是很明顯的。

  • 在non-blocking IO中,雖然進(jìn)程大部分時間都不會被block,但是它仍然要求進(jìn)程去主動的check,并且當(dāng)數(shù)據(jù)準(zhǔn)備完成以后,也需要進(jìn)程主動的再次調(diào)用recvfrom來將數(shù)據(jù)拷貝到用戶內(nèi)存。
  • asynchronous IO則完全不同。它就像是用戶進(jìn)程將整個IO操作交給了他人(kernel)完成,然后他人做完后發(fā)信號通知。在此期間,用戶進(jìn)程不需要去檢查IO操作的狀態(tài),也不需要主動的去拷貝數(shù)據(jù)。

IO復(fù)用技術(shù)

在IO編程過程中,當(dāng)需要處理多個請求時,可以使用多線程和IO復(fù)的方式進(jìn)行處理。

IO復(fù)用是什么?

把多個IO的阻塞復(fù)用到一個select之類的阻塞上,從而使得系統(tǒng)在單線程的情況下同時支持處理多個請求

IO復(fù)用常見的應(yīng)用場景:

  • 服務(wù)器需要同時處理多個處于監(jiān)聽狀態(tài)和多個連接狀態(tài)的套接字;
  • 服務(wù)器需要處理多種網(wǎng)絡(luò)協(xié)議的套接字
  • IO復(fù)用的實現(xiàn)方式目前主要有select、poll和epoll/evpoll。

select和poll的原理基本相同:

  1. 注冊待偵聽的fd(這里的fd創(chuàng)建時最好使用非阻塞)

  2. 每次調(diào)用都去檢查這些fd的狀態(tài),當(dāng)有一個或者多個fd就緒的時候返回

  3. 返回結(jié)果中包括已就緒和未就緒的fd

select和poll與epoll機(jī)制的比較

Linux網(wǎng)絡(luò)編程過程中,相比于select/poll,epoll是有著更明顯優(yōu)勢的一種選擇。

  • 支持一個進(jìn)程打開的socket描述符不受限制(僅受限于操作系統(tǒng)的最大文件句柄數(shù) unlimit)。

    • Select的缺陷:一個進(jìn)程所打開的FD受限,默認(rèn)是2048;盡管數(shù)值可以更改,但同樣可能導(dǎo)致網(wǎng)絡(luò)效率下降;可以選擇多進(jìn)程的解決方案,但是進(jìn)程的創(chuàng)建本身代價不小,而且進(jìn)程間數(shù)據(jù)同步遠(yuǎn)比不上線程間同步的高效。

    • epoll所支持的FD上限是最大可以打開文件的數(shù)目:/proc/sys/fs/file-max

IO效率可能隨著文件描述符數(shù)目的增加而線性下降。

  • epoll掃描系統(tǒng)的機(jī)制不同

    • select/poll是線性掃描FD的集合;

    • epoll是根據(jù)FD上面的回調(diào)函數(shù)實現(xiàn)的,活躍的socket會主動去調(diào)用該回調(diào)函數(shù),其它socket則不會,相當(dāng)于市是一個AIO,只不過推動力在OS內(nèi)核。

  • 使用mmap加速內(nèi)核與用戶空間的消息傳遞,zero-copy的一種。

  • epoll的API更加簡單。

  • IO復(fù)用還有一個 水平觸發(fā) 和 邊緣觸發(fā) 的概念:

    • 水平觸發(fā):當(dāng)就緒的fd未被用戶進(jìn)程處理后,下一次查詢依舊會返回,這是select和poll的觸發(fā)方式。
    • 邊緣觸發(fā):無論就緒的fd是否被處理,下一次不再返回。理論上性能更高,但是實現(xiàn)相當(dāng)復(fù)雜,并且任何意外的丟失事件都會造成請求處理錯誤。epoll默認(rèn)使用水平觸發(fā),通過相應(yīng)選項可以使用邊緣觸發(fā)。

IO模型的總結(jié)

最后,再舉幾個不是很恰當(dāng)?shù)睦觼碚f明這四個IO Model,有A,B,C,D四個人在釣魚:

  • A用的是最老式的魚竿,所以呢,得一直守著,等到魚上鉤了再拉桿;(同步阻塞)

  • B的魚竿有個功能,能夠顯示是否有魚上鉤,所以呢,B就和旁邊的MM聊天,隔會再看看有沒有魚上鉤,有的話就迅速拉桿;(非阻塞)

  • C用的魚竿和B差不多,但他想了一個好辦法,就是同時放好幾根魚竿,然后守在旁邊,一旦有顯示說魚上鉤了,它就將對應(yīng)的魚竿拉起來;(io多路復(fù)用機(jī)制)

  • D是個有錢人,干脆雇了一個人幫他釣魚,一旦那個人把魚釣上來了,就給D發(fā)個短信。(異步機(jī)制)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容