<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>

          深入聊聊Linux 五種IO模型

          共 5759字,需瀏覽 12分鐘

           ·

          2021-11-27 06:05



          一、相關概念講解


          1、同步與異步


          同步就是一個任務的完成需要依賴另外一個任務時,只有等待被依賴的任務完成后,依賴的任務才能算完成,這是一種可靠的任務序列。要么成功都成功,失敗都失敗,兩個任務的狀態(tài)可以保持一致。

          異步是不需要等待被依賴的任務完成,只是通知被依賴的任務要完成什么工作,依賴的任務也立即執(zhí)行,只要自己完成了整個任務就算完成了。至于被依賴的任務最終是否真正完成,依賴它的任務無法確定,所以它是不可靠的任務序列。


          2、堵塞與非堵塞


          阻塞和非阻塞這兩個概念與程序(線程)等待消息通知(無所謂同步或者異步)時的狀態(tài)有關。也就是說阻塞與非阻塞主要是程序(線程)等待消息通知時的狀態(tài)角度來說的。
          ?
          阻塞調(diào)用是指調(diào)用結果返回之前,當前線程會被掛起,一直處于等待消息通知,不能夠執(zhí)行其他業(yè)務。函數(shù)只有在得到結果之后才會返回。

          非阻塞和阻塞的概念相對應,指在不能立刻得到結果之前,該函數(shù)不會阻塞當前線程,而會立刻返回。雖然表面上看非阻塞的方式可以明顯的提高CPU的利用率,但是也帶了另外一種后果就是系統(tǒng)的線程切換增加。增加的CPU執(zhí)行時間能不能補償系統(tǒng)的切換成本需要好好評估。
          ?
          (a)?如果這個線程在等待當前函數(shù)返回時,仍在執(zhí)行其他消息處理,那這種情況就叫做同步非阻塞;

          (b)?如果這個線程在等待當前函數(shù)返回時,沒有執(zhí)行其他消息處理,而是處于掛起等待狀態(tài),那這種情況就叫做同步阻塞

          同步/異步關注的是消息通知的機制,而阻塞/非阻塞關注的是程序(線程)等待消息通知時的狀態(tài)


          3、用戶空間與內(nèi)核空間


          現(xiàn)在操作系統(tǒng)都是采用虛擬存儲器,那么對32位操作系統(tǒng)而言,它的尋址空間(虛擬存儲空間)為4G232次方)。操作系統(tǒng)的核心是內(nèi)核,獨立于普通的應用程序,可以訪問受保護的內(nèi)存空間,也有訪問底層硬件設備的所有權限。為了保證用戶進程不能直接操作內(nèi)核(kernel),保證內(nèi)核的安全,操作系統(tǒng)將虛擬空間劃分為兩部分,一部分為內(nèi)核空間,一部分為用戶空間。針對linux操作系統(tǒng)而言,將最高的1G字節(jié)(從虛擬地址0xC00000000xFFFFFFFF),供內(nèi)核使用,稱為內(nèi)核空間,而將較低的3G字節(jié)(從虛擬地址0x000000000xBFFFFFFF),供各個進程使用,稱為用戶空間。

          4、進程切換


          為了控制進程的執(zhí)行,內(nèi)核必須有能力掛起正在CPU上運行的進程,并恢復以前掛起的某個進程的執(zhí)行。這種行為被稱為進程切換。因此可以說,任何進程都是在操作系統(tǒng)內(nèi)核的支持下運行的,是與內(nèi)核緊密相關的。
          從一個進程的運行轉到另一個進程上運行,這個過程中經(jīng)過下面這些變化:

          1、保存處理機上下文,包括程序計數(shù)器和其他寄存器。
          2、更新PCB信息。
          3、把進程的PCB移入相應的隊列,如就緒、在某事件阻塞等隊列。
          4、選擇另一個進程執(zhí)行,并更新其PCB
          5、更新內(nèi)存管理的數(shù)據(jù)結構。
          6、恢復處理機上下文。
          注:總而言之就是很耗資源


          5、進程的堵塞


          正在執(zhí)行的進程,由于期待的某些事件未發(fā)生,如請求系統(tǒng)資源失敗、等待某種操作的完成、新數(shù)據(jù)尚未到達或無新工作做等,則由系統(tǒng)自動執(zhí)行阻塞原語(Block),使自己由運行狀態(tài)變?yōu)樽枞麪顟B(tài)。可見,進程的阻塞是進程自身的一種主動行為,也因此只有處于運行態(tài)的進程(獲得CPU),才可能將其轉為阻塞狀態(tài)。當進程進入阻塞狀態(tài),是不占用CPU資源的


          6、文件描述符


          文件描述符(File descriptor)是計算機科學中的一個術語,是一個用于表述指向文件的引用的抽象化概念。

          文件描述符在形式上是一個非負整數(shù)。實際上,它是一個索引值指向內(nèi)核為每一個進程所維護的該進程打開文件的記錄表。當程序打開一個現(xiàn)有文件或者創(chuàng)建一個新文件時,內(nèi)核向進程返回一個文件描述符。在程序設計中,一些涉及底層的程序編寫往往會圍繞著文件描述符展開。但是文件描述符這一概念往往只適用于UNIX、Linux這樣的操作系統(tǒng)。


          7、緩存


          緩存?IO?又被稱作標準?IO,大多數(shù)文件系統(tǒng)的默認?IO?操作都是緩存?IO。在?Linux?的緩存?IO?機制中,操作系統(tǒng)會將?IO?的數(shù)據(jù)緩存在文件系統(tǒng)的頁緩存(page cache?)中,也就是說,數(shù)據(jù)會先被拷貝到操作系統(tǒng)內(nèi)核的緩沖區(qū)中,然后才會從操作系統(tǒng)內(nèi)核的緩沖區(qū)拷貝到應用程序的地址空間。

          緩存 IO 的缺點:

          數(shù)據(jù)在傳輸過程中需要在應用程序地址空間和內(nèi)核進行多次數(shù)據(jù)拷貝操作,這些數(shù)據(jù)拷貝操作所帶來的 CPU 以及內(nèi)存開銷是非常大的。


          二、IO模型


          網(wǎng)絡IO的本質是socket的讀取,socketlinux系統(tǒng)被抽象為流,IO可以理解為對流的操作。剛才說了,對于一次IO訪問(以read舉例),數(shù)據(jù)會先被拷貝到操作系統(tǒng)內(nèi)核的緩沖區(qū)中,然后才會從操作系統(tǒng)內(nèi)核的緩沖區(qū)拷貝到應用程序的地址空間。

          所以說,當一個read操作發(fā)生時,它會經(jīng)歷兩個階段:

          第一階段:等待數(shù)據(jù)準備?(Waiting for the data to be ready)。
          第二階段:將數(shù)據(jù)從內(nèi)核拷貝到進程中?(Copying the data from the kernel to the process)。
          ?
          對于socket流而言,

          第一步:通常涉及等待網(wǎng)絡上的數(shù)據(jù)分組到達,然后被復制到內(nèi)核的某個緩沖區(qū)。
          第二步:把數(shù)據(jù)從內(nèi)核緩沖區(qū)復制到應用進程緩沖區(qū)。
          ?
          網(wǎng)絡應用需要處理的無非就是兩大類問題,網(wǎng)絡IO,數(shù)據(jù)計算。相對于后者,網(wǎng)絡IO的延遲,給應用帶來的性能瓶頸大于后者。

          網(wǎng)絡IO的模型大致有如下幾種:
          · 同步模型(synchronous IO
          · 阻塞IObloking IO
          · 非阻塞IOnon-blocking IO
          · 多路復用IOmultiplexing IO
          · 信號驅動式IOsignal-driven IO
          · 異步IOasynchronous IO注:由于signal driven IO在實際中并不常用,所以我這只提及剩下的四種IO Model。

          1、堵塞IO模型


          應用程序調(diào)用一個 IO 函數(shù),導致應用程序阻塞,等待數(shù)據(jù)準備好。?如果數(shù)據(jù)沒有準備好,一直等待…數(shù)據(jù)準備好了,從內(nèi)核拷貝到用戶空間,IO?函數(shù)返回成功指示。

          當調(diào)用 recv()函數(shù)時,系統(tǒng)首先查是否有準備好的數(shù)據(jù)。如果數(shù)據(jù)沒有準備好,那么系統(tǒng)就處于等待狀態(tài)。當數(shù)據(jù)準備好后,將數(shù)據(jù)從系統(tǒng)緩沖區(qū)復制到用戶空間,然后該函數(shù)返回。在套接應用程序中,當調(diào)用?recv()函數(shù)時,未必用戶空間就已經(jīng)存在數(shù)據(jù),那么此時?recv()函數(shù)就會處于等待狀態(tài)。
          ?

          2、非堵塞IO模型

          ?
          我們把一個?SOCKET?接口設置為非阻塞就是告訴內(nèi)核,當所請求的?I/O?操作無法完成時,不要將進程睡眠,而是返回一個錯誤。這樣我們的?I/O?操作函數(shù)將不斷的測試數(shù)據(jù)是否已經(jīng)準備好,如果沒有準備好,繼續(xù)測試,直到數(shù)據(jù)準備好為止。在這個不斷測試的過程中,會大量的占用?CPU?的時間。上述模型絕不被推薦。


          3、IO 復用模型

          ?
          由于同步非阻塞方式需要不斷主動輪詢,輪詢占據(jù)了很大一部分過程,輪詢會消耗大量的CPU時間,而 “后臺” 可能有多個任務在同時進行,人們就想到了循環(huán)查詢多個任務的完成狀態(tài),只要有任何一個任務完成,就去處理它。如果輪詢不是進程的用戶態(tài),而是有人幫忙就好了。那么這就是所謂的?IO?多路復用”
          ?
          IO多路復用有兩個特別的系統(tǒng)調(diào)用selectpollepoll函數(shù)。select調(diào)用是內(nèi)核級別的,select輪詢相對非阻塞的輪詢的區(qū)別在于---前者可以等待多個socket,能實現(xiàn)同時對多個IO端口進行監(jiān)聽,當其中任何一個socket的數(shù)據(jù)準好了,就能返回進行可讀,然后進程再進行recvform系統(tǒng)調(diào)用,將數(shù)據(jù)由內(nèi)核拷貝到用戶進程,當然這個過程是阻塞的。selectpoll調(diào)用之后,會阻塞進程,與blocking IO阻塞不同在于,此時的select不是等到socket數(shù)據(jù)全部到達再處理,?而是有了一部分數(shù)據(jù)就會調(diào)用用戶進程來處理。如何知道有一部分數(shù)據(jù)到達了呢?監(jiān)視的事情交給了內(nèi)核,內(nèi)核負責數(shù)據(jù)到達的處理。也可以理解為"非阻塞"吧。

          I/O復用模型會用到selectpollepoll函數(shù),這幾個函數(shù)也會使進程阻塞,但是和阻塞I/O所不同的的,這兩個函數(shù)可以同時阻塞多個I/O操作。而且可以同時對多個讀操作,多個寫操作的I/O函數(shù)進行檢測,直到有數(shù)據(jù)可讀或可寫時(注意不是全部數(shù)據(jù)可讀或可寫),才真正調(diào)用I/O操作函數(shù)。

          對于多路復用,也就是輪詢多個socket。多路復用既然可以處理多個IO,也就帶來了新的問題,多個IO之間的順序變得不確定了,當然也可以針對不同的編號。
          ?
          在I/O編程過程中,當需要同時處理多個客戶端接入請求時,可以利用多線程或者I/O多路復用技術進行處理。I/O多路復用技術通過把多個I/O的阻塞復用到同一個select的阻塞上,從而使得系統(tǒng)在單線程的情況下可以同時處理多個客戶端請求。與傳統(tǒng)的多線程/多進程模型比,I/O多路復用的最大優(yōu)勢是系統(tǒng)開銷小,系統(tǒng)不需要創(chuàng)建新的額外進程或者線程,也不需要維護這些進程和線程的運行,降底了系統(tǒng)的維護工作量,節(jié)省了系統(tǒng)資源,I/O多路復用的主要應用場景如下:

          1、服務器需要同時處理多個處于監(jiān)聽狀態(tài)或者多個連接狀態(tài)的套接字。
          2、服務器需要同時處理多種網(wǎng)絡協(xié)議的套接字。

          此時你是不是想到的了redis如何做的啊,redis用的就是多路復用。
          ?

          3、信號驅動IO

          ?

          簡介:兩次調(diào)用,兩次返回;

          首先我們允許套接口進行信號驅動 I/O,并安裝一個信號處理函數(shù),進程繼續(xù)運行并不阻塞。當數(shù)據(jù)準備好時,進程會收到一個?SIGIO?信號,可以在信號處理函數(shù)中調(diào)用?I/O?操作函數(shù)處理數(shù)據(jù)。


          4、異步IO模型

          ?
          相對于同步IO,異步IO不是順序執(zhí)行。用戶進程進行aio_read系統(tǒng)調(diào)用之后,無論內(nèi)核數(shù)據(jù)是否準備好,都會直接返回給用戶進程,然后用戶態(tài)進程可以去做別的事情。等到socket數(shù)據(jù)準備好了,內(nèi)核直接復制數(shù)據(jù)給進程,然后從內(nèi)核向進程發(fā)送通知。IO兩個階段,進程都是非阻塞的。

          Linux提供了AIO庫函數(shù)實現(xiàn)異步,但是用的很少。目前有很多開源的異步IO庫,例如libeventlibevlibuv
          ?
          ?

          5、5種I/O模型的比較

          ?
          不同?I/O?模型的區(qū)別,其實主要在等待數(shù)據(jù)和數(shù)據(jù)復制這兩個時間段不同,圖形中已經(jīng)表示得很清楚了。

          通過上面的圖片,可以發(fā)現(xiàn)non-blocking IO和asynchronous IO的區(qū)別還是很明顯的。在non-blocking IO中,雖然進程大部分時間都不會被block,但是它仍然要求進程去主動的check,并且當數(shù)據(jù)準備完成以后,也需要進程主動的再次調(diào)用recvfrom來將數(shù)據(jù)拷貝到用戶內(nèi)存。而asynchronous IO則完全不同。它就像是用戶進程將整個IO操作交給了他人(kernel)完成,然后他人做完后發(fā)信號通知。在此期間,用戶進程不需要去檢查IO操作的狀態(tài),也不需要主動的去拷貝數(shù)據(jù)。
          ?
          ?
          同步非阻塞方式相比同步阻塞方式:

          優(yōu)點:能夠在等待任務完成的時間里干其他活了(包括提交其他任務,也就是?“后臺” 可以有多個任務在同時執(zhí)行)。

          缺點:任務完成的響應延遲增大了,因為每過一段時間才去輪詢一次read操作,而任務可能在兩次輪詢之間的任意時間完成。這會導致整體數(shù)據(jù)吞吐量的降低。

          三、select 、poll 、epoll的區(qū)別?


          1、支持一個進程所能打開的最大連接數(shù)
          ?

          2、FD?(文件描述符)劇增后帶來的?IO?效率問題
          ?
          3、消息傳遞方式
          ?

          綜上,在選擇select,pollepoll?時要根據(jù)具體的使用場合以及這三種方式的自身特點。
          1、表面上看?epoll?的性能最好,但是在連接數(shù)少并且連接都十分活躍的情況下,select?和?poll?的性能可能比?epoll?好,畢竟?epoll?的通知機制需要很多函數(shù)回調(diào)。
          2、select?低效是因為每次它都需要輪詢。但低效也是相對的,視情況而定,也可通過良好的設計改善
          ?
          補充知識點:

          Level_triggered(水平觸發(fā)):當被監(jiān)控的文件描述符上有可讀寫事件發(fā)生時,epoll_wait()會通知處理程序去讀寫。如果這次沒有把數(shù)據(jù)一次性全部讀寫完(如讀寫緩沖區(qū)太小),那么下次調(diào)用?epoll_wait()時,它還會通知你在上沒讀寫完的文件描述符上繼續(xù)讀寫,當然如果你一直不去讀寫,它會一直通知你!如果系統(tǒng)中有大量你不需要讀寫的就緒文件描述符,而它們每次都會返回,這樣會大大降低處理程序檢索自己關心的就緒文件描述符的效率!

          Edge_triggered(邊緣觸發(fā)):當被監(jiān)控的文件描述符上有可讀寫事件發(fā)生時,epoll_wait()會通知處理程序去讀寫。如果這次沒有把數(shù)據(jù)全部讀寫完(如讀寫緩沖區(qū)太小),那么下次調(diào)用?epoll_wait()時,它不會通知你,也就是它只會通知你一次,直到該文件描述符上出現(xiàn)第二次可讀寫事件才會通知你!這種模式比水平觸發(fā)效率高,系統(tǒng)不會充斥大量你不關心的就緒文件描述符!

          select(),poll()模型都是水平觸發(fā)模式,信號驅動IO是邊緣觸發(fā)模式,epoll()模型即支持水平觸發(fā),也支持邊緣觸發(fā),默認是水平觸發(fā)。


          相關閱讀:




          轉載申明:轉載本號文章請注明作者來源,本號發(fā)布文章若存在版權等問題,請留言聯(lián)系處理,謝謝。


          推薦閱讀

          更多架構相關技術知識總結請參考“架構師全店鋪技術資料打包”相關電子書(37本技術資料打包匯總詳情可通過“閱讀原文”獲取)。

          全店內(nèi)容持續(xù)更新,現(xiàn)下單“全店鋪技術資料打包(全)”,后續(xù)可享全店內(nèi)容更新“免費”贈閱,價格僅收198元(原總價350元)。



          溫馨提示:

          掃描二維碼關注公眾號,點擊閱讀原文鏈接獲取架構師技術全店資料打包匯總(全)電子書資料詳情


          瀏覽 47
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <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>
                  俺去俺来也www色老 | 在线观看中文字幕一区 | 中国一级特黄大片学生 | 青草青草青青草 | 日本九九视频 |