【Linux網絡-五種IO模型與阻塞IO】

一、引入

網絡通信的本質就是進程間的通信,進程間通信的本質就是IO(Input,Output)

I/O(input/output)也就是輸入和輸出,在馮諾依曼體系結構當中,將數據從輸入設備拷貝到內存就叫作輸入,將數據從內存拷貝到輸出設備就叫作輸出

站在進程的角度,站在網絡的角度

  • 如何理解IO?

IO = 等 + 拷貝,我們在使用read/recv/send/write等,有數據的時候就拷貝到自己的或者對應的緩沖區,沒有數據的時候,就進行阻塞等待或者非阻塞等待

  • 什么叫做高效的IO?

本質就是單位時間內,減少等的比重

二、五種IO模型

1.例子引入

現在我們來談談釣魚,釣魚 = 等 + 釣,IO也是等 + 拷貝,我們現在借著釣魚的例子來理解五種IO模型

  • 現在有5個人去釣魚
  • 張三釣魚一直不動(別人與張三說話,張三也不理對方),眼睛一直盯著魚漂,魚漂動了,就拉起魚竿
  • 李四釣魚一直在動,沒事就刷刷抖音,和張三說說話(張三不理他),順便檢測魚漂,魚漂動了,就拉起魚竿
  • 王五釣魚在魚竿上掛了鈴鐺,魚一旦上鉤,鈴鐺就會響,就拉起魚竿
  • 趙六釣魚買了很多魚竿,把每根魚竿插在岸邊,一直在岸邊跑來跑去,任何一個魚竿就緒,就拉起魚竿
  • 田七釣魚帶來一個司機小王,但田七臨時有事離開釣魚塘,田七對小王說:漁具什么我全部給你準備好了,我在給你一個水桶,等你把水桶裝滿,你在打電話給我,然后回來;田七沒有參與調用,只負責發起釣魚

在IO中,這里的人可以看作系統調用、魚竿就是sockfd,釣魚塘是系統內部,魚就是數據,魚漂浮動就是數據就緒,釣就是發生拷貝

  • 張三,李四和王五的釣魚效率本質是一樣的嗎?

是的,因為他們釣魚方式都是一樣的,都是先等魚上鉤,然后再將魚釣上來

其次,他們每個人都只拿一根魚竿,在等待魚的上鉤,當河里魚來咬魚鉤的時候,這條魚咬哪一個魚鉤的概率是相同的

  • 誰的效率更高?

趙六,因為趙六減少了等待概率的發生,增加了拷貝的時間,所以他的效率是最高的

趙六的效率之所以高,是因為趙六一次等待多個魚竿的魚上鉤,可以將“等”的時間進行重疊

  • 如何看待田七的釣魚方式?

田七沒有參與調用,只負責發起釣魚;田七沒有參與等+拷貝的任意一項,而真正釣魚的是小王,在小王釣魚的期間,田七可以干任意的事情,如果將釣魚看作是一種 IO 的話,那么田七的這種釣魚方式就叫作異步 IO

  • 概念整理

張三:阻塞IO

李四:非阻塞IO

王五:信號驅動IO

趙六:多路復用/多路轉接IO

田七+小王:異步IO

阻塞IO與非阻塞IO的本質就是等的方式不同

【例子】在之前的echo例子中,鍵盤向OS輸入,實際將鍵盤輸入的數據放入到OS內部的輸入緩沖區,當進程需要這個數據的時候,將輸入緩沖區的內容拷貝到進程,進程執行結果后將數據拷貝到OS內部的輸出緩沖區,顯示器從輸出緩沖區拷貝內容,最終就把結果回顯給我們

在這里插入圖片描述

IO = 等 + 拷貝

多路轉接的作用就是為了等待多個fd,等待該fd上面的新事件(OS底層有數據了,讀時間就緒,或者OS底層有空間了,寫事件就緒)就緒,通知程序員,事件已經就緒,可以進行IO拷貝了

2.阻塞IO

阻塞IO:在內核將數據準備好之前,系統調用會一直等待;所有的套接字,默認都是阻塞方式

阻塞IO是最常見的IO模型

在這里插入圖片描述

應用進程通過recvfrom函數從某個套接字上讀取數據時,如果底層的數據沒有準備好,那么這個進程就一直在這個地方等待著,一旦數據就緒后,才會將數據從內核拷貝到用戶空間,最后recvfrom才會返回

這種以阻塞方式進行IO操作的進程或線程,在“等”和“拷貝”期間都不會返回,在用戶看來就是阻塞了,因此被稱為阻塞IO

3.非阻塞IO

如果內核還未將數據準備好,系統調用仍然會直接返回,并且返回EWOULDBLOCK錯誤碼

非阻塞IO往往需要程序員循環的方式反復嘗試讀寫文件描述符,這個過程稱為輪詢,這對CPU來說是較大的浪費,一般只有特定場景下才使用

在這里插入圖片描述

當調用recvfrom函數以非阻塞方式從某個套接字上讀取數據時,如果底層數據沒有準備好,那么recvfrom就會立馬錯誤返回,而不是讓該進程進行阻塞等待

因為沒有讀取數據,所以該進程或線程后續還需要繼續調用recvfrom函數,檢測底層數據是否就緒,如果沒有就繼續錯誤返回,直到監測到底層有數據后,再將數據從內核拷貝到用戶空間,再進行成功返回

阻塞與非阻塞的區別就是,非阻塞可以去做其他事情,而阻塞就一直在等

fcntl

在Linux操作系統中,fcntl() 函數是一個用于文件控制的系統調用。它允許你以不同的方式操作打開的文件描述符。這個函數接受三個參數:

fd:要操作的文件描述符。

cmd:指定要執行的文件控制命令。

...:根據 cmd 命令的不同,可能需要傳遞額外的參數。

cmd 參數值用途
F_DUPFD復制文件描述符,返回一個新的文件描述符,它是當前最低可用文件描述符。
F_GETFD獲取文件描述符的close-on-exec標志。
F_SETFD設置文件描述符的close-on-exec標志。
F_GETFL獲取文件狀態標志和訪問模式(如O_RDONLY, O_WRONLY, O_RDWR)。
F_SETFL設置文件狀態標志,如O_APPEND, O_NONBLOCK等。
F_GETLK獲取記錄鎖。
F_SETLK設置或釋放記錄鎖(非阻塞)。
F_SETLKW設置或釋放記錄鎖(阻塞)。

將指定的文件描述符設置為非阻塞模式

void SetNonBlock(int fd)
{int fl = ::fcntl(fd, F_GETFL);if(fl < 0){std::cout << "fcntl error" << std::endl;return;}::fcntl(fd, F_SETFL, fl | O_NONBLOCK);
}

下面代碼演示了非阻塞能夠收到EWOULDBLOCK返回

并且也能區分error是出錯了 還是因為非阻塞返回的

#include <iostream>
#include <cstdio>
#include <unistd.h>
#include "Comm.hpp"#include <sys/select.h>int main()
{char buffer[1024];SetNonBlock(0);while(true){// printf("Enter# ");// fflush(stdout);ssize_t n = ::read(0, buffer, sizeof(buffer)-1);if(n > 0){buffer[n] = 0;printf("echo# %s", buffer);}else if(n == 0)  // ctrl + d{printf("read done\n");break;}else{// 如果是非阻塞,底層數據沒有就緒,IO接口,會以出錯形式返回// 所以,如何區分  底層不就緒  vs   真的出錯了? 根據errno錯誤碼if(errno == EWOULDBLOCK){sleep(1);std::cout << "底層數據沒有就緒,開始輪詢檢測" << std::endl;std::cout << "可以做其他事情" << std::endl;// do other thingcontinue;}else if(errno == EINTR){continue;}else{perror("read");break;}// perror("read\n"); // printf("n=%ld\n", n);// //底層數據沒有就緒: errno 會被設置成為 EWOULDBLOCK EAGAIN// printf("errno=%d\n", errno); // break;}}return 0;
}

測試結果:
當我們輸入數據,而不按回車的時候,底層仍然在輪詢檢測,當我們輸入回車的時候;echo出來的內容與我們輸入的內容一致

在這里插入圖片描述

4.信號驅動

內核將數據準備好的時候,使用SIGIO信號通知應用程序進行IO操作

在這里插入圖片描述

應用程序通過系統調用sigaction來設置一個SIGIO信號的處理函數。這個處理函數將在接收到SIGIO信號時被觸發。內核處于等待狀態,直到數據準備好。數據準備好時,內核會發出一個SIGIO信號通知應用程序。應用程序捕獲到SIGIO信號后,它會執行recvfrom系統調用來從網絡接收數據,recvfrom系統調用完成后,內核會將控制權交還給應用程序,同時傳遞回成功的指示,數據報從內核空間復制到用戶空間,應用程序現在可以在用戶空間內處理收到的數據報了

如果數據正在從內核空間拷貝到用戶空間的緩沖區過程中,那么在此期間,應用程序可能會暫時阻塞,直到數據拷貝完成。

信號的產生是異步的,但信號驅動 IO 是同步 IO 的一種。

因為它依然參與了等 + 拷貝

5.IO多路轉接

雖然從流程圖上看起來和阻塞IO類似,實際上最核心在于IO多路轉接能夠同時等待多個文件描述符的就緒狀態

使用select最主要的目的:將等 + 拷貝兩個操作分開。select專門負責等,recvfrom負責拷貝

在這里插入圖片描述

  • 應用程序通過調用select函數來阻塞自己,等待多個套接字中的任何一個變為可讀狀態。這意味著應用程序會暫停執行,直到至少一個套接字準備好讀取數據。
  • 當操作系統檢測到某個套接字的數據已經準備好可以讀取時,它會通知應用程序,并通過select函數返回這個信息。
  • 應用程序收到操作系統的通知后,它可以通過recvfrom系統調用來實際從套接字中讀取數據。這個調用會將數據從網絡層復制到應用程序指定的緩沖區中。
  • 內核負責管理數據的接收、存儲以及最終傳遞給應用程序的過程。具體來說,當數據到達內核的網絡堆棧時,內核會將其暫存起來,然后根據應用程序的要求進行相應的處理。
  • 數據被內核成功接收并準備好供應用程序讀取的狀態。
  • 內核將數據從其內部緩存(通常稱為“內核空間”)拷貝到應用程序分配的用戶空間內存區域
  • 數據拷貝完成后,應用程序就可以訪問這些數據并進行進一步的處理了。

因為這些多路轉接接口是一次 “等” 多個文件描述符的,因此能將 “等” 的時間重疊,數據就緒后再調用對應的 recvfrom 等函數進行數據的拷貝,此時這些函數就能夠直接進行拷貝,而不需要 “等” 了

6.異步IO

由內核在數據拷貝完成時,通知應用程序(而信號驅動是告訴應用程序何時可以開始拷貝數據)。

在這里插入圖片描述

應用進程調用aio_read函數發起一個異步讀操作。內核檢查數據是否準備好供讀取(如果數據未準備好,內核會等待直到數據準備好;一旦數據準備好,內核會將數據拷貝到用戶空間緩沖區中)當數據被成功拷貝到用戶空間時,內核通知應用程序數據已經可用,應用程序可以繼續執行其他任務,而不需要等待I/O操作的完成,當I/O操作完成后,內核通過信號或回調函數通知應用程序。

7.小結

任何IO過程中,都包含兩個步驟,第一個是等待,第二是拷貝,而且在實際的應用場景中,等待消耗的時間往往都遠遠高于拷貝的時間。讓IO更高效,最核心的辦法就是讓等待的時間盡量少

三、高級IO重要概念

1.同步通信 VS 異步通信(Synchronous Communication / Asynchronous Communication)

同步和異步關注的是消息通信機制

  • 所謂同步,就是在發出一個調用時,在沒有得到結果之前,該調用就不返回。但是一旦調用返回,就得到返回值了。換句話說,就是由調用者主動等待這個調用的結果。
  • 異步則是相反,調用在發出之后,這個調用就直接返回了,所以沒有返回結果。換句話說,當一個異步過程調用發出后,調用者不會立刻得到結果。而是在調用發出后,被調用者通過狀態、通知來通知調用者,或通過回調函數處理這個調用。

另外,我們回憶在講多進程多線程的時候,也提到同步和互斥,這里的同步通信和進程之間的同步是完全不想干的概念

  • 進程 / 線程同步:指的是在保證數據安全的前提下,讓進程/線程能夠按照某種特定的順序訪問臨界資源,從而有效避免饑餓問題,談論的是進程/線程間的一種工作關系。
  • 同步 IO:指的是進程/線程與操作系統之間的關系,談論的是進程/線程是否需要主動參與 IO 過程。

注意:尤其是在訪問臨界資源的時候,一定要弄清楚這個 “同步”,是同步通信異步通信的同步,還是同步與互斥的同步。

2.阻塞 VS 非阻塞

阻塞和非阻塞關注的是程序在等待調用結果(消息,返回值)時的狀態。

  • 阻塞調用是指調用結果返回之前,當前線程會被掛起。調用線程只有在得到結果之后才會返回。
  • 非阻塞調用指在不能立刻得到結果之前,該調用不會阻塞當前線程。

3.其他高級 IO

非阻塞 IO、 紀錄鎖、系統 V 流機制、 I/O 多路轉接(也叫 I/O 多路復用), readv 和 writev 函數以及存儲映射 IO( mmap ),這些統稱為高級 IO。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/74286.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/74286.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/74286.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

算法-最大公約數

1、約數&#xff1a; 1.1 試除法求約數 原理&#xff1a;只需要遍歷最小的約數即可&#xff0c;較大的那個可以直接算出來。 import java.util.*; public class Main {static Scanner sc new Scanner(System.in);public static void main(String[] args) {int t sc.nextIn…

湖北楚大夫

品牌出海已成為眾多企業拓展業務、提升競爭力的關鍵戰略。楚大夫(chudafu.com)作為一家專注于品牌出海、海外網絡營銷推廣以及外貿獨立站搭建的公司&#xff0c;憑借其專業、高效、創新的服務模式&#xff0c;致力于成為中國企業走向國際市場的堅實后盾與得力伙伴。楚大夫通過綜…

Flutter 學習之旅 之 flutter 使用 connectivity_plus 進行網路狀態監聽(斷網/網絡恢復事件監聽)

Flutter 學習之旅 之 flutter 使用 connectivity_plus 進行網路狀態監聽&#xff08;斷網/網絡恢復事件監聽&#xff09; 目錄 Flutter 學習之旅 之 flutter 使用 connectivity_plus 進行網路狀態監聽&#xff08;斷網/網絡恢復事件監聽&#xff09; 一、簡單介紹 二、conne…

從零開始實現 C++ TinyWebServer 處理請求 HttpRequest類詳解

文章目錄 HTTP 請求報文HttpRequest 類實現 Init() 函數實現 ParseRequestLine() 函數實現 ParseHeader() 函數實現 ParsePath() 函數實現 ParseBody() 函數實現 ParsePost() 函數實現 ParseFromUrlEncoded() 函數實現 UserVerify() 函數實現 Parse() 函數HttpRequest 代碼Http…

systemd-networkd 的 *.network 配置文件詳解 筆記250323

systemd-networkd 的 *.network 配置文件詳解 筆記250323 查看官方文檔可以用 man systemd.network命令, 或訪問: https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html 名稱 systemd.network — 網絡配置 概要 network.network 描述 一個純…

自定義mavlink 生成wireshark wlua插件錯誤(已解決)

進入正題 python3 -m pymavlink.tools.mavgen --langWLua --wire-protocol2.0 --outputoutput/develop message_definitions/v1.0/development.xml 編譯WLUA的時候遇到一些問題 1.ERROR:SCHEMASV:SCHEMAV_CVC_ENUMERATION_VALID 3765:0:ERROR:SCHEMASV:SCHEMAV_CVC_ENUMERAT…

計算機操作系統(四) 操作系統的結構與系統調用

計算機操作系統&#xff08;四&#xff09; 操作系統的結構與系統調用 前言一、操作系統的結構1.1 簡單結構1.2 模塊化結構1.3 分層化結構1.4 微內核結構1.5 外核結構 二、系統調用1.1 系統調用的基本概念1.2 系統調用的類型 總結&#xff08;核心概念速記&#xff09;&#xf…

深入解析 Spring IOC AOP:原理、源碼與實戰

深入解析 Spring IOC & AOP&#xff1a;原理、源碼與實戰 Spring 框架的核心在于 IOC&#xff08;控制反轉&#xff09; 和 AOP&#xff08;面向切面編程&#xff09;。今天&#xff0c;我們將深入剖析它們的原理&#xff0c;結合源碼解析&#xff0c;并通過 Java 代碼實戰…

LLM之RAG理論(十四)| RAG 最佳實踐

RAG 的過程很復雜&#xff0c;包含許多組成部分。我們如何確定現有的 RAG 方法及其最佳組合&#xff0c;以確定最佳 RAG 實踐&#xff1f; 論文 《Searching for Best Practices in Retrieval-Augmented Generation》給出了回答。 本文將從以下三方面進行介紹&#xff1a; 首先…

利用knn算法實現手寫數字分類

利用knn算法實現手寫數字分類 1.作者介紹2.KNN算法2.1KNN&#xff08;K-Nearest Neighbors&#xff09;算法核心思想2.2KNN算法的工作流程2.3優缺點2.4 KNN算法圖示介紹 3.實驗過程3.1安裝所需庫3.2 MNIST數據集3.3 導入手寫數字圖像進行分類3.4 完整代碼3.5 實驗結果 1.作者介…

C語言-適配器模式詳解與實踐

文章目錄 C語言適配器模式詳解與實踐1. 什么是適配器模式&#xff1f;2. 為什么需要適配器模式&#xff1f;3. 實際應用場景4. 代碼實現4.1 UML 關系圖4.2 頭文件 (sensor_adapter.h)4.3 實現文件 (sensor_adapter.c)4.4 使用示例 (main.c) 5. 代碼分析5.1 關鍵設計點5.2 實現特…

Rust函數、條件語句、循環

文章目錄 函數**語句與表達式**條件語句循環 函數 Rust的函數基本形式是這樣的 fn a_func(a: i32) -> i32 {}函數名是蛇形風格&#xff0c;rust不在意函數的聲明順序&#xff0c;只需要有聲明即可 函數參數必須聲明參數名稱和類型 語句與表達式 這是rust非常重要的基礎…

maptalks圖層交互 - 模擬 Tooltip

maptalks圖層交互 - 模擬 Tooltip 圖層交互-模擬tooltip官方文檔 <!DOCTYPE html> <html><meta charsetUTF-8 /><meta nameviewport contentwidthdevice-width, initial-scale1 /><title>圖層交互 - 模擬 Tooltip</title><style typet…

好吧好吧,看一下達夢的模式與用戶的關系

單憑個人感覺&#xff0c;模式在達夢中屬于邏輯對象合集&#xff0c;回頭再看資料 應該是一個用戶可以對應多個模式 問題來了&#xff0c;模式的ID和用戶的ID一樣嗎&#xff1f; 不一樣 SELECT USER_ID,USERNAME FROM DBA_USERS WHERE USERNAMETEST1; SELECT ID AS SCHID, NA…

python socket模塊學習記錄

python黑馬程序員 通過python內置socket模塊&#xff0c;在電腦本地開發一個服務器&#xff0c;一個客戶端&#xff0c;連接后進行連續的聊天。服務器和客戶端均可輸入exit&#xff0c;主動退出連接。 服務器開發.py import socket# 創建Socket對象 socket_server socket.s…

7-2 sdut-C語言實驗-逆序建立鏈表

7-2 sdut-C語言實驗-逆序建立鏈表 分數 20 全屏瀏覽 切換布局 作者 馬新娟 單位 山東理工大學 輸入整數個數N&#xff0c;再輸入N個整數&#xff0c;按照這些整數輸入的相反順序建立單鏈表&#xff0c;并依次遍歷輸出單鏈表的數據。 輸入格式: 第一行輸入整數N;&#xff…

針對永磁電機(PMM)的d軸和q軸電流,考慮交叉耦合補償,設計P1控制器并推導出相應的傳遞函數

電流控制回路:針對永磁電機(PMM)的d軸和q軸電流&#xff0c;考慮交叉耦合補償&#xff0c;設計P1控制器并推導出相應的傳遞函數。 1. 永磁電機&#xff08;PMM&#xff09;的數學模型 在同步旋轉坐標系&#xff08; d ? q d - q d?q 坐標系&#xff09;下&#xff0c;永磁同…

ROS多機通信(四)——Ubuntu 網卡 Mesh 模式配置指南

引言 使用Ad-hoc加路由協議和直接Mesh模式配置網卡實現的網絡結構是一樣的&#xff0c;主要是看應用選擇&#xff0c; Ad-Hoc模式 B.A.T.M.A.N. / OLSR 優點&#xff1a;靈活性高&#xff0c;適合移動性強或需要優化的復雜網絡。 缺點&#xff1a;配置復雜&#xff0c;需手動…

chap1:統計學習方法概論

第1章 統計學習方法概論 文章目錄 第1章 統計學習方法概論前言章節目錄導讀 實現統計學習方法的步驟統計學習分類基本分類監督學習無監督學習強化學習 按模型分類概率模型與非概率模型 按算法分類按技巧分類貝葉斯學習核方法 統計學習方法三要素模型模型是什么? 策略損失函數與…

爬蟲案例-爬取某站視頻

文章目錄 1、下載FFmpeg2、爬取代碼3、效果圖 1、下載FFmpeg FFmpeg是一套可以用來記錄、轉換數字音頻、視頻&#xff0c;并能將其轉化為流的開源計算機程序。 點擊下載: ffmpeg 安裝并配置 FFmpeg 步驟&#xff1a; 1.下載 FFmpeg&#xff1a; 2.訪問 FFmpeg 官網。 3.選擇 Wi…