<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          一舉拿下網(wǎng)絡(luò) IO 模型

          共 2900字,需瀏覽 6分鐘

           ·

          2021-11-07 03:59

          前言

          IO 是計(jì)算機(jī)體系中重要的一部分?。不同的 IO 設(shè)備有著不同的特點(diǎn):數(shù)據(jù)率不一樣、傳送單位不一樣,數(shù)據(jù)表示不一樣,等等。所以,很難實(shí)現(xiàn)一種統(tǒng)一的輸入輸出方法。

          IO 有兩種操作,同步 IO 和異步 IO。同步 IO 指的是,必須等待 IO 操作完成后,控制權(quán)才返回給用戶進(jìn)程。異步 IO 是,無須等待 IO 操作完成,就將控制權(quán)返回給用戶進(jìn)程。


          上面就是一個(gè)典型的阻塞 IO,對(duì)方還沒有準(zhǔn)備好回啥,發(fā)送只能等著,知道對(duì)方想好回復(fù)語,再進(jìn)行回復(fù)。下面學(xué)習(xí)一下常見的 4 種 IO 模型。

          阻塞 IO 模型

          在Linux ,默認(rèn)情況下所有的 socket 都是阻塞的,一個(gè)典型的讀操作流程如圖所示。

          阻塞和非阻塞的概念描述的是用戶線程調(diào)用內(nèi)核 IO 操作的方式:阻塞是指 IO 操作需要徹底完成后才返回到用戶空間;而非阻塞是指 IO 操作被調(diào)用后立即返回給用戶一個(gè)狀態(tài)值,不需要等到 IO 操作徹底完成。

          當(dāng)應(yīng)用進(jìn)程調(diào)用了 recvfrom 這個(gè)系統(tǒng)調(diào)用后,系統(tǒng)內(nèi)核就開始了 IO 的第一個(gè)階段 :準(zhǔn)備數(shù)據(jù)

          對(duì)于網(wǎng)絡(luò) IO 來說,很多時(shí)候數(shù)據(jù)在一開始還沒到達(dá)時(shí),系統(tǒng)內(nèi)核就要等待足夠的數(shù)據(jù)到來。而在用戶進(jìn)程這邊,整個(gè)進(jìn)程會(huì)被阻塞。

          當(dāng)系統(tǒng)內(nèi)核一直等到數(shù)據(jù)準(zhǔn)備好了,它就會(huì)將數(shù)據(jù)從系統(tǒng)內(nèi)核中拷貝到用戶內(nèi)存中,然后系統(tǒng)內(nèi)核返回結(jié)果,用戶進(jìn)程才解除阻塞的狀態(tài),重新運(yùn)行起來。所以,阻塞 IO 模型的特點(diǎn)就是
          IO 執(zhí)行的兩個(gè)階段都被阻塞了。

          大部分的 socke 接口都是阻塞型的。所謂阻塞型接口是指系統(tǒng)調(diào)用時(shí)卻不返回調(diào)用結(jié)果,并讓當(dāng)前線程一直處于阻塞狀態(tài),只有當(dāng)該系統(tǒng)調(diào)用獲得結(jié)果或者超時(shí)出錯(cuò)時(shí)才返回結(jié)果。

          實(shí)際上,除非特別指定,幾乎所有的 IO 接口都阻塞型的。這給網(wǎng)絡(luò)編程帶來了一個(gè)很大的問題,如在調(diào)用 send 的同時(shí),線程處于阻塞狀態(tài),則在此期間,線程將無法執(zhí)行任何運(yùn)算或響應(yīng)任何網(wǎng)絡(luò)請求。


          非阻塞?IO 模型

          在Linux 下,可以通過設(shè)置 socket IO 變?yōu)榉亲枞麪顟B(tài)。當(dāng)一個(gè)非阻塞的 socket執(zhí)行 read 操作時(shí),流程如圖:

          當(dāng)用戶進(jìn)程發(fā)出 read 操作時(shí),如果內(nèi)核中的數(shù)據(jù)還沒有準(zhǔn)備好,那么它并不會(huì) block 用戶進(jìn)程,而是立刻返回一個(gè)錯(cuò)誤。

          從用戶進(jìn)程角度講,它發(fā)起 read 操作后,并不需要等待,而是馬上就得到了一個(gè)結(jié)果 當(dāng)用戶進(jìn)程判斷結(jié)果是一個(gè)錯(cuò)誤時(shí),它就知道數(shù)據(jù)還沒有準(zhǔn)備好,于是它可以再次發(fā)送 read 操作。

          一旦內(nèi)核中的數(shù)據(jù)準(zhǔn)備好了,并且又再次收到了用戶進(jìn)程的系統(tǒng)調(diào)用,那么它馬上就將數(shù)據(jù)復(fù)制到了用戶內(nèi)存中,然后返回正確的返回值。

          所以,在非阻塞式 IO 中,用戶進(jìn)程其實(shí)需要不斷地主動(dòng)詢問 kernel 數(shù)據(jù)是否準(zhǔn)備好。非阻塞的接口相比于阻塞型接口的顯著差異在于被調(diào)用之后立即返回,使用如下的函數(shù)可以將某句柄歸設(shè)為非阻塞狀態(tài):fcntl( fd , F_SETFL , O_NONBLOCK);

          在非阻塞狀態(tài)下,recv 接口在被調(diào)用后立即返回,返回值代表了不同的含義,如下所述。

          • recv 返回值大于 0,表示接收數(shù)據(jù)完畢,返回值即是接收到的字節(jié)數(shù)。

          • recv 返回 0,表示連接已經(jīng)正常斷開。

          • recv 返回 -1 ,且 errno 等于 EAGAIN ,表示 recv 操作還沒執(zhí)行完成。

          • recv 返回?-1,且 errno 不等于 EAGAIN ,表示 recv 操作遇到系統(tǒng)錯(cuò)誤 errno。

          可以看到服務(wù)器線程可以通過循環(huán)調(diào)用 recv 接口,可以在單個(gè)線程內(nèi)實(shí)現(xiàn)對(duì)所有連接的數(shù)據(jù)接收。但是上述模型絕不被推薦,因?yàn)檠h(huán)調(diào)用 recv 將大幅度占用 CPU 使用率

          此外,在這個(gè)方案 recv 更多的是起到檢測“操作是否完成”的作用,實(shí)際操作系統(tǒng)提供了更為高效的檢測“操作是否完成”作用的接口,例如 select 多路復(fù)用模式,可以次檢測多個(gè)連接是存活躍。

          多路 IO 復(fù)用模型

          多路 IO 復(fù)用,有時(shí)也稱為事件驅(qū)動(dòng) IO。它的基本原理就是有個(gè)函數(shù)會(huì)不斷地輪詢所負(fù)責(zé)的所有 socket ,當(dāng)某個(gè) socket 有數(shù)據(jù)到達(dá)了,就通知用戶進(jìn)程。IO 復(fù)用模型的流程如圖:

          當(dāng)用戶進(jìn)程調(diào)用了 select ,那么整個(gè)進(jìn)程會(huì)被阻塞,而同時(shí),內(nèi)核會(huì) "監(jiān)視" 所有 select 負(fù)責(zé)的 socket ,當(dāng)任何一個(gè) socket 中的數(shù)據(jù)準(zhǔn)備好了, select 就會(huì)返回。這個(gè)時(shí)候用戶進(jìn)程再調(diào)用 read 操作,將數(shù)據(jù)從內(nèi)核拷貝到用戶進(jìn)程。

          這個(gè)模型和阻塞 IO 的模型其實(shí)并沒有太大的不同,事實(shí)上還更差一些 因?yàn)檫@里需要使用兩個(gè)系統(tǒng)調(diào)用,而阻塞 IO 只調(diào)用了一個(gè)系統(tǒng)調(diào)用 recvfrom,用 select 的優(yōu)勢在于它可以同時(shí)處理多個(gè)連接

          如果處理的連接數(shù)不是很高的話,使用 select/epoll Web server 定比使用多線程的阻塞 IO Web server 性能更好,可能延遲還更大;select/poll 的優(yōu)勢并不是對(duì)于單個(gè)連接能處理得更快,而是在于能處理更多的連接

          異步 IO 模型

          上面是異步 IO 模型。

          用戶進(jìn)程發(fā)起 read 操作之后,立刻就可以開始去做其他的事;而另一方面,從內(nèi)核的角度,當(dāng)它收到一個(gè)異步的 read 請求操作之后,首先會(huì)立刻返回,所以不會(huì)對(duì)用戶進(jìn)程產(chǎn)生任何阻塞。

          然后,內(nèi)核會(huì)等待數(shù)據(jù)準(zhǔn)備完成,然后將數(shù)據(jù)拷貝到用戶內(nèi)存中,當(dāng)這一切都完成之后,
          內(nèi)核會(huì)給用戶進(jìn)程發(fā)送一個(gè)信號(hào),返回 read 操作已完成的信息。

          調(diào)用阻塞 IO 一直阻塞住對(duì)應(yīng)的進(jìn)程直到操作完成,而非阻塞 IO 在內(nèi)核還在準(zhǔn)備數(shù)據(jù)的情況下會(huì)立刻返回。兩者的區(qū)別就在于同步 IO 進(jìn)行 IO 操作時(shí)會(huì)阻塞進(jìn)程。

          非阻塞 IO 在執(zhí)行 recvfrom 這個(gè)系統(tǒng)調(diào)用的時(shí)候,如果內(nèi)核的數(shù)據(jù)沒有準(zhǔn)備好,這時(shí)候不會(huì)阻塞進(jìn)程。但是當(dāng)內(nèi)核中數(shù)據(jù)準(zhǔn)備好時(shí),
          recvfrom 會(huì)將數(shù)據(jù)從內(nèi)核拷貝到用戶內(nèi)存中,這個(gè)時(shí)候進(jìn)程則被阻塞。

          而異步 IO 則不 樣,當(dāng)進(jìn)程發(fā)起 IO 操作之后,就直接返回,直到內(nèi)核發(fā)送一個(gè)信號(hào),告訴進(jìn)程 IO 已完成,則在這整個(gè)過程中,
          進(jìn)程完全沒有被阻塞

          絮叨

          經(jīng)過上面的學(xué)習(xí),你會(huì)發(fā)現(xiàn)非阻塞 IO 和異步 IO 的區(qū)別還是很明顯的。

          在非阻塞 IO 中,雖然進(jìn)程大部分時(shí)間都不會(huì)被阻塞,但是它仍然要求進(jìn)程去
          主動(dòng)檢查,并且當(dāng)數(shù)據(jù)準(zhǔn)備完成以后,也需要進(jìn)程主動(dòng)地再次調(diào)用 recvfrom 來將數(shù)據(jù)拷貝到用戶內(nèi)存中。

          而異步IO 則完全不同,它就像是用戶進(jìn)程將整個(gè) IO 操作交給了內(nèi)核完成,然后內(nèi)核做完后
          發(fā)信號(hào)通知。


          IO 作為計(jì)算機(jī)的基礎(chǔ)知識(shí),后臺(tái)開發(fā)務(wù)必要掌握。更多網(wǎng)絡(luò)編程相關(guān)的知識(shí)可以去學(xué)習(xí) unix 網(wǎng)絡(luò)編程,祝大家學(xué)習(xí)愉快!

          ·················?END?··················

          點(diǎn)擊關(guān)注公眾號(hào),免費(fèi)領(lǐng)學(xué)習(xí)資料

          瀏覽 30
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日本黄色免费 | 青青操青青操 | 热视频亚洲欧美 | 国产精品久久久久久久久免费挑花 | 人人草人人摸人人看 |