認識網路IO模型

来源:https://www.cnblogs.com/CoderGan/archive/2022/07/18/16488894.html
-Advertisement-
Play Games

硬體準備: CH32V103 開發板/核心版, WCH-Link. 軟體準備: 軟體主要是用於編譯的 RISC-V GCC , 和用於燒錄的 OpenOCD., RISC-V GCC 可以選擇公版或者WCH版, OpenOCD 暫時只能用WCH定製版本, 用公版的無法識別 wlink ...


  • BIO模型

    在Linux中,預設情況下所有socket都是阻塞模式。用戶線程調用系統函數read()【system call】,內核開始準備數據(從磁碟/網路獲取數據),內核准備數據完成後,用戶線程完成數據從內核拷貝到用戶空間的應用程式緩衝區,數據拷貝完成後,請求才返回。從發起read請求到完成內核到應用程式的拷貝,整個過程都是阻塞的。

    為了減輕線程阻塞的弊端,實際上,每個Read/Write請求都會分配單獨線程進行單獨處理。在低併發時期,這種每個請求每線程的處理方式是可以應付的,但是如果在高併發期間(如:業務高峰期),就會分配大量的線程完成請求處理。因此會帶來非常大的性能損耗。
  • NIO模型

    用戶線程在發起Read請求後立即返回,不用等待內核准備數據的過程。如果Read請求沒讀取到數據,用戶線程會不斷輪詢發起Read請求,直到數據到達(內核准備好數據)後才停止輪詢。
    非阻塞IO模型雖然避免了由於線程阻塞問題帶來的大量線程消耗,但是頻繁的重覆輪詢大大增加了請求次數,對CPU消耗也比較明顯。
  • 多路復用模型

    多路復用IO模型,建立在多路事件分離函數select,poll,epoll之上。在發起read請求前,先更新select的socket監控列表,然後等待select(或poll或epoll)函數返回(此過程是阻塞的)。當某個socket有數據到達時,select函數返回。此時用戶線程才正式發起read或write請求,處理數據。這種模式用一個專門的監視線程去檢查多個socket,如果某個socket有數據到達就交給工作線程處理。由於等待Socket數據到達過程非常耗時,所以這種方式解決了阻塞IO模型一個Socket連接就需要一個線程的問題,也不存在非阻塞IO模型忙輪詢帶來的CPU性能損耗的問題。

    多路復用的本質,在我看來其實就是通過儘可能少(預期一次)的系統調用(system call),就可以拿到所有socket的狀態(是否可讀),然後程式只需要對那些返回狀態為可讀或可寫的socket進行處理。

     1 // NIO核心代碼
     2 // 初始化
     3 channel = ServerSocketChannel.open();
     4 channel.bind(port);
     5 channel.configureBlocking(false);
     6 // selector 註冊accept事件
     7 selector = Selector.open(); 
     8 channel.register(selector, SelectionKey.OP_ACCEPT);
     9 while(true){
    10     while(selector.select(timeout)>0){ // 有新的事件
    11         // 獲取到可處理的 Socket
    12         Set<SelectionKey> keySet = selector.selectedKeys();
    13         Iterator<SelectionKey> iter = keySet.iterator();
    14         while (iter.hasNext()) {    // 迴圈處理,處理之後應該從迭代器中移除
    15             SelectionKey key = iter.next();
    16             iter.remove();
    17             if (key.isAcceptable()) {    // 新連接 acceptHandle(key); }
    18             else if (key.isReadable()) {  // 讀事件 readHandle(key);  }
    19             else if (key.isWritable()) {  // 寫事件 writeHandle(key);  }
    20     }
    21 }
    22 }
    View Code

    實際上,linux的多路復用有三種實現方式,select和poll以及epoll,它們之間的關係是進化關係,性能都是遞進的。

    • select

      select 是操作系統提供的系統調用函數,通過它,我們可以把一個文件描述符的數組【最大為1024個】發給操作系統內核, 讓內核去遍歷,確定哪個文件描述符可以讀寫,實際上只是打了一個標誌,哪些可讀可寫,所以在用戶程式獲取select的返回值的時候,仍然需要遍歷文件描述符的數組具有哪些可處理的事件。

      select模型的優化在於將所有的文件描述符【其實就是正在監聽的socket連接】批量的傳給了內核,降低了系統調用,減少了內核態與用戶態的切換。而select的弊端就在於每次最多只能傳輸1024個文件描述符,這在一些高併發場景【redis緩存】下,仍然是不夠看的。此外select 在內核層仍然是通過遍歷的方式檢查文件描述符的就緒狀態,是個同步過程,只不過無系統調用切換上下文的開銷。
    • poll

      poll對於select來講,最大的區別在於只是將每次只能傳輸1024個文件描述符的限制去掉了
    • epoll

      epoll是在select和poll的基礎上做出的演進。他主要針對以下3點做出了改進
        1.內核中保存一份文件描述符的集合,無需用戶程式每次懂重新傳遞【不需要拷貝】;
        2.內核不再通過輪訓的方式獲取事件就緒的文件描述符,而是通過事件回調的方式將就緒事件放入到一個就緒隊列中【內核程式不需要進行O(N)的遍歷】;
        3.內核僅會將就緒隊列中的文件描述符返回給用戶程式,用戶程式直接處理該描述符對應的事件即可【用戶程式不需要再次進行O(N)的遍歷,內核將不必要的文件描述符過濾了,因此發生的記憶體拷貝更少了】。

    • select、poll、epoll對比

      AIO

      不論是阻塞IO亦或是NIO,它們都是同步IO,即當內核將數據準備好的時候,都是由用戶線程將數據拷貝到用戶空間。除此之外還有一個非同步IO,即非阻塞IO,當數據準備好的時候,不需要用戶線程將數據拷貝到程式的運行空間,而是直接由內核線程完成數據的拷貝。


      參考文件:
      1.徹底搞懂IO多路復用
      2.忘了一些重要的文章~~~


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 數組 數組存儲相同類型值的序列。 聲明數組 數組是一種數據結構,用來存儲同一類型值的集合。通過一個整型下標(index,或稱索引)可以訪問數組中的每一個值。例如,如果a是一個整型數組,a[i]就是數組中下標為i的整數。 在聲明數組變數時,需要指出數組類型(數據元素類型緊跟[])和數組變數的名字。下麵 ...
  • 啥是倍增思想? 倍增,每次將範圍擴大或減少一倍而達到加速的效果 舉個慄子,你想要跳到15米遠的地方,你怎麼找到這個15這個地方,一步一步跳嗎,利用倍增的話 預設一個k使2^k>15值 ,這裡我們假設k=5, 2^5=32 >15 k- -; k=4; 跳過了,不跳 2^4=16 >15 k- -; ...
  • 大數 如果基本的整數和浮點數精度不能夠滿足需求,那麼可以使用java.math包中兩個很有用的類:BigInteger和BigDecimal。這兩個類可以處理包含任意長度數字序列的數值。BigInteger類實現任意精度的整數運算,BigDecimal實現任意精度的浮點數運算。 使用靜態的value ...
  • Java 異常機制(也許是全網最獨特視角) 一、Java中的“異常“指什麼 什麼是異常 一句話簡單理解:異常是程式運行中的一些異常或者錯誤。 (純字面意思) Error類 和 Exception類 Java中“萬物皆對象”,異常也不例外, Java把異常當做對象來處理,並將異常分為兩大類——Erro ...
  • 一、業務需求 在工作中遇到一個場景,軟體bug或功能發佈之後,會通知測試進行測試,要求寫一個小工具能自動發送郵件,功能包含發送和抄送支持多個,因為只是通知沒有寫進附件功能,這個其他博客都有搜一下就可以了。 二、以下是實現代碼 這裡要註意如果接收者郵箱三種方式都沒配置則需要手動輸入,其他的沒配置就是彈 ...
  • 值類型 整數,浮點數,布爾值,字元,枚舉,結構體 引用類型 數組,用戶自定義的類,介面,委托,object,字元串 值類型與引用類型的區別: 存放地址不同,值類型存放在棧中,引用類型存放在堆中。 記憶體分佈: 在程式運行時,記憶體分別為四個區域塊,分別是:堆區,棧區,全局區,代碼區 存放函數內的局部變數 ...
  • 類是模板 對象是基於模板生成的實體 Car表示類,car表示變數,new關鍵字,Car()類的構造方法 Car car = new Car(); 構造方法:new欄位後跟的方法,用於創建一個對象 成員變數:類中聲明的變數稱為這個類的成員變數,類的成員變數需要對象.變數 成員方法:類中聲明的方法稱為這 ...
  • 三目運算符一般用在不是是就是否的結果上,和if else用法基本相似但先比較之下代碼量更上,熟悉過後也是比較容易上手的。 三目運算符 點擊查看代碼 // 獲得二者之間的最小值 public int GetMinValue(int numberA,int numberB){ // 含義:`numebr ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...