在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

從I/O的阻塞與非阻塞、I/O處理的單線程與多線程角度探討服務器模型

馬哥Linux運維 ? 2018-01-08 16:13 ? 次閱讀

前言的前言

服務器模型涉及到線程模式和IO模式,搞清楚這些就能針對各種場景有的放矢。該系列分成三部分:

單線程/多線程阻塞I/O模型

單線程非阻塞I/O模型

多線程非阻塞I/O模型,Reactor及其改進

前言

這里探討的服務器模型主要指的是服務器端對I/O的處理模型。從不同維度可以有不同的分類,這里從I/O的阻塞與非阻塞、I/O處理的單線程與多線程角度探討服務器模型。

對于I/O,可以分成阻塞I/O與非阻塞I/O兩大類型。阻塞I/O在做I/O讀寫操作時會使當前線程進入阻塞狀態,而非阻塞I/O則不進入阻塞狀態。

對于線程,單線程情況下由一條線程負責所有客戶端連接的I/O操作,而多線程情況下則由若干線程共同處理所有客戶端連接的I/O操作。

單線程阻塞I/O模型

單線程阻塞I/O模型是最簡單的一種服務器模型,幾乎所有程序員在剛開始接觸網絡編程時都從這個簡單的模型開始。這種模型只能同時處理一個客戶端訪問,并且在I/O操作上是阻塞的,線程會一直在等待,而不會做其他事情。對于多個客戶端訪問,必須要等到前一個客戶端訪問結束才能進行下一個訪問的處理,請求一個一個排隊,只提供一問一答服務。

首先,服務器必須初始化一個套接字服務器,并綁定某個端口號并使之監聽客戶端的訪問。接著,客戶端1調用服務器的服務,服務器接收到請求后對其進行處理,處理完后寫數據回客戶端1,整個過程都是在一個線程里面完成的。最后,處理客戶端2的請求并寫數據回客戶端2,期間就算客戶端2在服務器處理完客戶端1之前就進行請求,也要等服務器對客戶端1響應完后才會對客戶端2進行響應處理。

這種模型的特點在于單線程和阻塞I/O。單線程即服務器端只有一個線程處理客戶端的所有請求,客戶端連接與服務器端的處理線程比是n:1,它無法同時處理多個連接,只能串行處理連接。而阻塞I/O是指服務器在讀寫數據時是阻塞的,讀取客戶端數據時要等待客戶端發送數據并且把操作系統內核復制到用戶進程中,這時才解除阻塞狀態。寫數據回客戶端時要等待用戶進程將數據寫入內核并發送到客戶端后才解除阻塞狀態。這種阻塞給網絡編程帶來了一個問題,服務器必須要等到客戶端成功接收才能繼續往下處理另外一個客戶端的請求,在此期間線程將無法響應任何客戶端請求。

該模型的特點:它是最簡單的服務器模型,整個運行過程都只有一個線程,只能支持同時處理一個客戶端的請求(如果有多個客戶端訪問,就必須排隊等待),服務器系統資源消耗較小,但并發能力低,容錯能力差。

多線程阻塞I/O模型

針對單線程阻塞I/O模型的缺點,我們可以使用多線程對其進行改進,使之能并發地對多個客戶端同時進行響應。多線程模型的核心就是利用多線程機制為每個客戶端分配一個線程。服務器端開始監聽客戶端的訪問,假如有兩個客戶端發送請求過來,服務器端在接收到客戶端請求后分別創建兩個線程對它們進行處理,每條線程負責一個客戶端連接,直到響應完成。期間兩個線程并發地為各自對應的客戶端處理請求,包括讀取客戶端數據、處理客戶端數據、寫數據回客戶端等操作。

這種模型的I/O操作也是阻塞的,因為每個線程執行到讀取或寫入操作時都將進入阻塞狀態,直到讀取到客戶端的數據或數據成功寫入客戶端后才解除阻塞狀態。盡管I/O操作阻塞,但這種模式比單線程處理的性能明顯高了,它不用等到第一個請求處理完才處理第二個,而是并發地處理客戶端請求,客戶端連接與服務器端處理線程的比例是1:1。

多線程阻塞I/O模型的特點:支持對多個客戶端并發響應,處理能力得到大幅提高,有較大的并發量,但服務器系統資源消耗量較大,而且多線程之間會產生線程切換成本,同時擁有較復雜的結構。

單線程非阻塞I/O模型

多線程阻塞I/O模型通過引入多線程確實提高了服務器端的并發處理能力,但每個連接都需要一個線程負責I/O操作。當連接數量較多時可能導致機器線程數量太多,而這些線程大多數時間卻處于等待狀態,造成極大的資源浪費。鑒于多線程阻塞I/O模型的缺點,有沒有可能用一個線程就可以維護多個客戶端連接并且不會阻塞在讀寫操作呢?下面介紹單線程非阻塞I/O模型。

單線程非阻塞I/O模型最重要的一個特點是,在調用讀取或寫入接口后立即返回,而不會進入阻塞狀態。在探討單線程非阻塞I/O模型前必須要先了解非阻塞情況下套接字事件的檢測機制,因為對于單線程非阻塞模型最重要的事情是檢測哪些連接有感興趣的事件發生。一般會有如下三種檢測方式。

應用程序遍歷套接字的事件檢測

當多個客戶端向服務器請求時,服務器端會保存一個套接字連接列表中,應用層線程對套接字列表輪詢嘗試讀取或寫入。對于讀取操作,如果成功讀取到若干數據,則對讀取到的數據進行處理;如果讀取失敗,則下一個循環再繼續嘗試。對于寫入操作,先嘗試將數據寫入指定的某個套接字,寫入失敗則下一個循環再繼續嘗試。

這樣看來,不管有多少個套接字連接,它們都可以被一個線程管理,一個線程負責遍歷這些套接字列表,不斷地嘗試讀取或寫入數據。這很好地利用了阻塞的時間,處理能力得到提升。但這種模型需要在應用程序中遍歷所有的套接字列表,同時需要處理數據的拼接,連接空閑時可能也會占用較多CPU資源,不適合實際使用。對此改進的方法是使用事件驅動的非阻塞方式。

內核遍歷套接字的事件檢測

這種方式將套接字的遍歷工作交給了操作系統內核,把對套接字遍歷的結果組織成一系列的事件列表并返回應用層處理。對于應用層,它們需要處理的對象就是這些事件,這就是其中一種事件驅動的非阻塞方式的實現。

服務器端有多個客戶端連接,應用層向內核請求讀寫事件列表。內核遍歷所有套接字并生成對應的可讀列表readList和可寫列表writeList。readList標明了每個套接字是否可讀,例如套接字1的值為1,表示可讀,socket2的值為0,表示不可讀。writeList則標明了每個套接字是否可寫。應用層遍歷讀寫事件列表readList和writeList,做相應的讀寫操作。

內核遍歷套接字時已經不用在應用層對所有套接字進行遍歷,將遍歷工作下移到內核層,這種方式有助于提高檢測效率。然而,它需要將所有連接的可讀事件列表和可寫事件列表傳到應用層,假如套接字連接數量變大,列表從內核復制到應用層也是不小的開銷。另外,當活躍連接較少時,內核與應用層之間存在很多無效的數據副本,因為它將活躍和不活躍的連接狀態都復制到應用層中。

內核基于回調的事件檢測

通過遍歷的方式檢測套接字是否可讀可寫是一種效率比較低的方式,不管是在應用層中遍歷還是在內核中遍歷。所以需要另外一種機制來優化遍歷的方式,那就是回調函數。內核中的套接字都對應一個回調函數,當客戶端往套接字發送數據時,內核從網卡接收數據后就會調用回調函數,在回調函數中維護事件列表,應用層獲取此事件列表即可得到所有感興趣的事件。

內核基于回調的事件檢測方式有兩種。第一種是用可讀列表readList和可寫列表writeList標記讀寫事件,套接字的數量與readList和writeList兩個列表的長度一樣,readList第一個元素標為1則表示套接字1可讀,同理,writeList第二個元素標為1則表示套接字2可寫。如圖所示,多個客戶端連接服務器端,當客戶端發送數據過來時,內核從網卡復制數據成功后調用回調函數將readList第一個元素置為1,應用層發送請求讀、寫事件列表,返回內核包含了事件標識的readList和writeList事件列表,進而分表遍歷讀事件列表readList和寫事件列表writeList,對置為1的元素對應的套接字進行讀或寫操作。這樣就避免了遍歷套接字的操作,但仍然有大量無用的數據(狀態為0的元素)從內核復制到應用層中。于是就有了第二種事件檢測方式。

內核基于回調的事件檢測方式二如圖所示。服務器端有多個客戶端套接字連接。首先,應用層告訴內核每個套接字感興趣的事件。接著,當客戶端發送數據過來時,對應會有一個回調函數,內核從網卡復制數據成功后即調回調函數將套接字1作為可讀事件event1加入到事件列表。同樣地,內核發現網卡可寫時就將套接字2作為可寫事件event2添加到事件列表中。最后,應用層向內核請求讀、寫事件列表,內核將包含了event1和event2的事件列表返回應用層,應用層通過遍歷事件列表得知套接字1有數據待讀取,于是進行讀操作,而套接字2則可以寫入數據。

上面兩種方式由操作系統內核維護客戶端的所有連接并通過回調函數不斷更新事件列表,而應用層線程只要遍歷這些事件列表即可知道可讀取或可寫入的連接,進而對這些連接進行讀寫操作,極大提高了檢測效率,自然處理能力也更強。

對于Java來說,非阻塞I/O的實現完全是基于操作系統內核的非阻塞I/O,它將操作系統的非阻塞I/O的差異屏蔽并提供統一的API,讓我們不必關心操作系統。JDK會幫我們選擇非阻塞I/O的實現方式,例如對于Linux系統,在支持epoll的情況下JDK會優先選擇用epoll實現Java的非阻塞I/O。這種非阻塞方式的事件檢測機制就是效率最高的“內核基于回調的事件檢測”中的第二種方式。

在了解了非阻塞模式下的事件檢測方式后,重新回到對單線程非阻塞I/O模型的討論。雖然只有一個線程,但是它通過把非阻塞讀寫操作與上面幾種檢測機制配合就可以實現對多個連接的及時處理,而不會因為某個連接的阻塞操作導致其他連接無法處理。在客戶端連接大多數都保持活躍的情況下,這個線程會一直循環處理這些連接,它很好地利用了阻塞的時間,大大提高了這個線程的執行效率。

單線程非阻塞I/O模型的主要優勢體現在對多個連接的管理,一般在同時需要處理多個連接的發場景中會使用非阻塞NIO模式,此模型下只通過一個線程去維護和處理連接,這樣大大提高了機器的效率。一般服務器端才會使用NIO模式,而對于客戶端,出于方便及習慣,可使用阻塞模式的套接字進行通信

多線程非阻塞I/O模型

單線程非阻塞I/O模型已經大大提高了機器的效率,而在多核的機器上可以通過多線程繼續提高機器效率。最樸實、最自然的做法就是將客戶端連接按組分配給若干線程,每個線程負責處理對應組內的連接。如圖所示,有4個客戶端訪問服務器,服務器將套接字1和套接字2交由線程1管理,而線程2則管理套接字3和套接字4,通過事件檢測及非阻塞讀寫就可以讓每個線程都能高效處理。

最經典的多線程非阻塞I/O模型方式是Reactor模式。首先看單線程下的Reactor,Reactor將服務器端的整個處理過程分成若干個事件,例如分為接收事件、讀事件、寫事件、執行事件等。Reactor通過事件檢測機制將這些事件分發給不同處理器去處理。如圖所示,若干客戶端連接訪問服務器端,Reactor負責檢測各種事件并分發到處理器,這些處理器包括接收連接的accept處理器、讀數據的read處理器、寫數據的write處理器以及執行邏輯的process處理器。在整個過程中只要有待處理的事件存在,即可以讓Reactor線程不斷往下執行,而不會阻塞在某處,所以處理效率很高。

基于單線程Reactor模型,根據實際使用場景,把它改進成多線程模式。常見的有兩種方式:一種是在耗時的process處理器中引入多線程,如使用線程池;另一種是直接使用多個Reactor實例,每個Reactor實例對應一個線程。

Reactor模式的一種改進方式如圖所示。其整體結構基本上與單線程的Reactor類似,只是引入了一個線程池。由于對連接的接收、對數據的讀取和對數據的寫入等操作基本上都耗時較少,因此把它們都放到Reactor線程中處理。然而,對于邏輯處理可能比較耗時的工作,可以在process處理器中引入線程池,process處理器自己不執行任務,而是交給線程池,從而在Reactor線程中避免了耗時的操作。將耗時的操作轉移到線程池中后,盡管Reactor只有一個線程,它也能保證Reactor的高效。

Reactor模式的另一種改進方式如圖所示。其中有多個Reactor實例,每個Reactor實例對應一個線程。因為接收事件是相對于服務器端而言的,所以客戶端的連接接收工作統一由一個accept處理器負責,accept處理器會將接收的客戶端連接均勻分配給所有Reactor實例,每個Reactor實例負責處理分配到該Reactor上的客戶端連接,包括連接的讀數據、寫數據和邏輯處理。這就是多Reactor實例的原理。

多線程非阻塞I/O模式讓服務器端處理能力得到很大提高,它充分利用機器的CPU,適合用于處理高并發的場景,但它也讓程序更復雜,更容易出現問題。

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 服務器
    +關注

    關注

    12

    文章

    9582

    瀏覽量

    86937
  • 多線程
    +關注

    關注

    0

    文章

    279

    瀏覽量

    20229
  • 阻塞
    +關注

    關注

    0

    文章

    24

    瀏覽量

    8219
  • 非阻塞
    +關注

    關注

    0

    文章

    13

    瀏覽量

    2237
  • 單線程
    +關注

    關注

    0

    文章

    18

    瀏覽量

    1815

原文標題:最全服務器模型詳解——從單線程阻塞到多線程非阻塞

文章出處:【微信號:magedu-Linux,微信公眾號:馬哥Linux運維】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    i.MX6ULL】驅動開發9——Linux I/O模型分析并進來類比

    本篇介紹了Linux中的五種I/O模型阻塞I/O模型
    的頭像 發表于 05-26 09:05 ?2435次閱讀
    【<b class='flag-5'>i</b>.MX6ULL】驅動開發9——Linux <b class='flag-5'>I</b>/<b class='flag-5'>O</b><b class='flag-5'>模型</b>分析并進來類比

    i.MX6ULL】驅動開發10—阻塞&amp;阻塞式按鍵檢測

    本篇使用兩種I/O模型進行按鍵讀取:阻塞I/O和非用阻塞
    的頭像 發表于 05-27 09:08 ?4328次閱讀
    【<b class='flag-5'>i</b>.MX6ULL】驅動開發10—<b class='flag-5'>阻塞</b>&amp;<b class='flag-5'>非</b><b class='flag-5'>阻塞</b>式按鍵檢測

    鴻蒙原生應用開發-ArkTS語言基礎類庫多線程I/O密集型任務開發

    使用異步并發可以解決單次I/O任務阻塞的問題,但是如果遇到I/O密集型任務,同樣會阻塞
    發表于 03-21 14:57

    Linux設備驅動中的阻塞阻塞I/O

    阻塞阻塞I/O阻塞操作是指在執行設備操作時若不能獲得資源則掛起進程,直到滿足可操作的條件后再
    發表于 02-21 10:53

    阻塞阻塞I/O詳解

    :Wake_up_interruptible(&q); 輪詢操作 輪詢的概念與作用使用阻塞I/O的應用程序通常會使用select()和poll()系統調用查詢是否可對設備進行無
    發表于 07-04 07:23

    阻塞阻塞I/O

    :Wake_up_interruptible(&q); 輪詢操作 輪詢的概念與作用使用阻塞I/O的應用程序通常會使用select()和poll()系統調用查詢是否可對設備進行無
    發表于 07-09 08:19

    使用QEMU運行RT-Thread多線程阻塞網絡編程

    本文描述了使用 QEMU 運行 RT-Thread 提供的基于多線程阻塞 socket 編程示例。
    發表于 03-30 07:43

    LabVIEW中使用多線程運行速度是否會更快

    是操作系統的工作,它并不能總是保證應用程序的不同線程運行在不同的處理器上。無論是單處理器還是多處理器系統,如果在耗CPU的線程和耗
    發表于 02-01 13:14

    探討一下Linux系統下的五種I/O模型

    ajax請求(異步): 請求通過事件觸發-》服務器處理(這是瀏覽仍然可以作其他事情)-》處理完畢  阻塞  
    發表于 08-23 16:35

    Java多線程總結之Queue

    在Java多線程應用中,隊列的使用率很高,多數生產消費模型的首選數據結構就是隊列。Java提供的線程安全的Queue可以分為 阻塞隊列和
    發表于 11-28 16:14 ?3448次閱讀
    Java<b class='flag-5'>多線程</b>總結之Queue

    多線程好還是單線程好?單線程多線程的區別 優缺點分析

    摘要:如今單線程多線程已經得到普遍運用,那么到底多線程好還是單線程好呢?單線程多線程的區別又
    發表于 12-08 09:33 ?8.2w次閱讀

    Nodejs搭建的異步阻塞服務器與傳統的阻塞多線程服務器區別

    技術,采用一系列的阻塞庫來支持事件循環的方式,將谷歌GoogleV8引擎提供的阻塞I/O棧與
    發表于 12-13 14:46 ?3次下載
    Nodejs搭建的異步<b class='flag-5'>非</b><b class='flag-5'>阻塞</b><b class='flag-5'>服務器</b>與傳統的<b class='flag-5'>阻塞</b><b class='flag-5'>多線程</b><b class='flag-5'>服務器</b>區別

    多線程服務器編程模型:如何正確使用mutex 和condition variable

    本文對多線程服務器的常用編程模型進行了一個詳細的解讀,本文中的多線程服務器是運行在 Linux 操作系統上網絡應用程序。介紹了典型的
    的頭像 發表于 02-19 08:29 ?7504次閱讀
    <b class='flag-5'>多線程</b><b class='flag-5'>服務器</b>編程<b class='flag-5'>模型</b>:如何正確使用mutex 和condition variable

    Redis為何選擇單線程

    在于網絡 I/O,也就是客戶端和服務端之間的網絡傳輸延遲,因此 Redis選擇了單線程I/O
    的頭像 發表于 10-09 10:59 ?484次閱讀

    鴻蒙OS開發實例:【ArkTS類庫多線程I/O密集型任務開發】

    使用異步并發可以解決單次I/O任務阻塞的問題,但是如果遇到I/O密集型任務,同樣會阻塞
    的頭像 發表于 04-01 16:32 ?721次閱讀
    鴻蒙OS開發實例:【ArkTS類庫<b class='flag-5'>多線程</b><b class='flag-5'>I</b>/<b class='flag-5'>O</b>密集型任務開發】
    主站蜘蛛池模板: 奇米奇米 | 日日噜噜噜夜夜爽爽狠狠视频 | 女人69xxx | 99久久国产免费福利 | 日韩三级久久 | 亚洲综合天堂网 | 欧美高清在线播放 | 欧美性视频一区二区三区 | 四虎国产精品永久在线看 | 国产性videostv另类极品 | 久久久久99精品成人片三人毛片 | 97人摸人人澡人人人超一碰 | 欧美在线免费 | 免费的色视频 | 亚洲天堂免费看 | 久久久精品免费观看 | 国产免费播放一区二区三区 | 婷婷六月综合 | 久热国产精品视频 | 在线黄色网| 日本三级成人午夜视频网 | 九九热在线视频观看这里只有精品 | 国产精品大片天天看片 | 91国在线啪精品一区 | 天天综合色网 | 国产网站黄 | 狠狠色噜噜狠狠狠97影音先锋 | 国产清纯白嫩大学生正在播放 | 日韩美毛片 | 天天做夜夜爽 | 热re久久精品国产99热 | 毛片又大又粗又长又硬 | 天天做天天添婷婷我也去 | www色多多| 青青热久免费精品视频在线观看 | 国产亚洲精品aa在线观看 | 久久ww| 免费看h视频 | 日产精品卡二卡三卡四卡无卡乱码 | riav久久中文一区二区 | 亚洲欧美天堂网 |