[linux倉庫]性能加速的隱形引擎:深度解析Linux文件IO中的緩沖區奧秘

🌟?各位看官好,我是egoist2023

🌍?Linux == Linux is not Unix !

🚀?今天來學習C語言緩沖區和內核緩存區的區別以及緩存類型。

👍?如果覺得這篇文章有幫助,歡迎您一鍵三連,分享給更多人哦!

目錄

書接上文

引入

什么是緩沖區

為什么要引入緩沖區

緩沖類型

FILE結構體

擴展

cout和cerr分開

將文件內核緩沖區刷新到磁盤

語言級緩沖區的意義

總結


書接上文

上文我們深入拆解了 Linux “一切皆文件” 設計哲學的核心支撐:從進程task_struct中的files_struct結構出發,明確了文件描述符(FD)作為 “數組下標” 的本質與分配規則;更通過struct filefile_operations結構體的分析,揭示了 Linux 如何以 “上層統一接口(如read/write)、下層設備差異化實現” 的 “類 C++ 多態” 思路,讓進程能以一致視角訪問異構資源,極大簡化了開發復雜度。

然而,當我們聚焦于這些統一接口的實際 I/O 執行細節時會發現:用戶空間與內核空間的頻繁切換、高速 CPU 與低速外設(磁盤、網絡卡等)的速度鴻溝、以及 “單次小數據 I/O” 帶來的硬件調度低效,都會成為制約資源訪問性能的關鍵瓶頸。為了解決這些矛盾、在 “統一接口” 的基礎上進一步優化 I/O 效率,Linux 引入了緩沖區(Buffer/Cache)?機制 —— 它既是銜接 “用戶進程” 與 “底層文件 / 設備” 的核心中間層,也是 “一切皆文件” 哲學在性能層面的重要延伸。接下來,我們將深入解析緩沖區的設計邏輯、實現機制及其在 I/O 流程中的關鍵作用。

引入

根據前面所學,一個進程要打開文件必須要通過OS進行打開,操作系統為了管理所打開的?件,都會為這個?件創建?個file結構體,內部就要加載一個文件的屬性和內容,屬性加載到文件屬性?,內容加載到文件緩沖區中。這里的緩沖區指的是文件內核緩沖區。

進程·柒章節,我們講了三種退出場景,對exit和_exit做了對比,exit屬于庫函數,終止進程時會主動刷新緩沖區。這里的緩沖區指的是C語言庫提供的緩沖區。

什么是緩沖區

緩沖區是內存空間的?部分。也就是說,在內存空間中預留了?定的存儲空間,這些存儲空間?來緩沖輸?或輸出的數據,這部分預留的空間就叫做緩沖區。緩沖區根據其對應的是輸?設備還是輸出設備,分為輸?緩沖區和輸出緩沖區。

簡單來說,緩沖區的本質就是一段內存空間

為什么要引入緩沖區

這里提供一段小故事供大家進行理解:

事件一:假期之余,張三買了一臺大疆action5pro的運動相機記錄生活。李四恰巧這幾天要出外旅游,打算租臺相機拍拍vlog,聽聞朋友張三買了臺action5pro。李四給張三呼電話:老朋友啊!聽聞你這幾天買了臺相機,這幾天打算出外玩玩,可否借你相機一用。熱情的張三對朋友也是極為真誠,直接答應了,說道:李四啊!在家里等我一下,我現在開車就把相機送你那。

張三到李四的家費了半天的時間。

事件二:待張三回來后,發現街拍套裝忘給李四送過去了,但此時他自己也有事情要做。張三想了一想,要不把自己的東西放菜鳥驛站上寄送,由快遞員代替他講街拍套裝送到李四那的菜鳥驛站。

快遞員從張三的菜鳥驛站到李四的菜鳥驛站也是需要花費半天的時間。只不過這個動作主體由張三替換成了快遞員。這意味著什么呢?張三可以在這段本是要他送貨的時間轉移到了快遞員,允許自己做更多的工作了。

事件三:我們清楚菜鳥驛站的貨并不是一收到貨就馬上派送的,可以等貨多了再進行派送。

相機和街拍套裝就是數據,是緩存的角色;

菜鳥驛站就是所謂的緩沖區;

將數據給菜鳥本質就是拷貝;

菜鳥驛站允許等貨多了再進行派送就是允許數據在緩沖區中積壓。這樣做的目的一次就可以刷新多次數據,變相減少IO次數。

讀寫?件時,如果不會開辟對?件操作的緩沖區,直接通過系統調?對磁盤進?操作(讀、寫等),那么每次對?件進??次讀寫操作時,都需要使?讀寫系統調?來處理此操作,即需要執??次系統調?,執??次系統調?將涉及到CPU狀態的切換,即從??空間切換到內核空間,實現進程上下?的切換,這將損耗?定的CPU時間,頻繁的磁盤訪問對程序的執?效率造成很?的影響。

為了減少使?系統調?的次數,提?效率,我們就可以采?緩沖機制。?如我們從磁盤?取信息,可以在磁盤?件進?操作時,可以?次從?件中讀出?量的數據到緩沖區中,以后對這部分的訪問就不需要再使?系統調?了,等緩沖區的數據取完后再去磁盤中讀取,這樣就可以減少磁盤的讀寫次數,再加上計算機對緩沖區的操作? 快于對磁盤的操作,故應?緩沖區可? 提?計算機的運?速度。??如,我們使?打印機打印?檔,由于打印機的打印速度相對較慢,我們先把?檔輸出到打印機相應的緩沖區,打印機再??逐步打印,這時我們的CPU可以處理別的事情。可以看出,緩沖區就是?塊內存區,它?在輸?輸出設備和CPU之間,?來緩存數據。它使得低速的輸?輸出設備和?速的CPU能夠協調?作,避免低速的輸?輸出設備占?CPU,解放出CPU,使其能夠?效率?作。

緩沖類型

由于我們的緩沖區是允許數據進行積壓的,那么肯定有自身的一套積壓規則存在:

標準I/O提供了3種類型的緩沖區。

  • 全緩沖區:這種緩沖?式要求填滿整個緩沖區后才進?I/O系統調?操作。對于磁盤?件的操作通常使?全緩沖的?式訪問。
  • ?緩沖區:在?緩沖情況下,當在輸?和輸出中遇到換?符時,標準I/O庫函數將會執?系統調?操作。當所操作的流涉及?個終端時(例如標準輸?和標準輸出),使??緩沖?式。因為標準I/O庫每?的緩沖區?度是固定的,所以只要填滿了緩沖區,即使還沒有遇到換?符,也會執?I/O系統調?操作,默認?緩沖區的??為1024。
  • ?緩沖區:?緩沖區是指標準I/O庫不對字符進?緩存,直接調?系統調?。標準出錯流stderr通常是不帶緩沖區的,這使得出錯信息能夠盡快地顯示出來。

除了上述列舉的默認刷新?式,下列特殊情況也會引發緩沖區的刷新:

  1. 緩沖區滿時;

  2. 執行fflush語句;

驗證stderr是否帶緩沖區:

int main() 
{close(2);int fd = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);if (fd < 0) {perror("open");return 0;}perror("hello world");close(fd);return 0;
}

將2號?件描述符重定向??件,由于stderr沒有緩沖區,“hello world”不?fflush就可以寫??件。

cat log.txt

hello world : Success

FILE結構體

printf("hello linux,hello everyone!");
sleep(3);
return 0;

在上面這段代碼中,printf執行完畢后,并未打印到顯示器上,而是3s后顯示。那么在sleep期間,我們的數據在哪呢?毫無疑問,在緩存區中。

是什么緩沖區呢?C語言庫提供的緩沖區。那這個緩沖區又在哪呢?FILE結構體中。

  1. struct FILE 本質是一個結構體
  2. C語言上,輸入輸出格式化
  3. C訪問文件,都是通過FILE訪問的,包括stdin,stdout, stderr!

FILE結構體內部為我們維護:語言級別的緩沖區空間!

struct FILE
{int fd;char *inbuffer;char *outbuffer;
}

如何理解printf,scanf 的格式化過程?

int a = 123456;
printf("%d", a);
  1. 格式化
  2. 格式化結果刷新到FILE緩沖區中
  3. 檢測是否需要刷新
  4. 滿足條件時調用write系統調用刷新到文件內核緩沖區中

文件內核緩沖區的刷新方式比較特殊,是單獨的執行流,這里不做考慮。

由此我們也可以回答上一章節留下的疑惑,為什么close該文件后,并沒有顯示打印的內容。

int main()
{close(1);int fd = open("log.txt",O_CREAT | O_WRONLY | O_TRUNC,0666);if(fd<0){perror("fd");return 1;}printf("hello file,fd:%d\n",fd); // stdout -> 1close(fd);return 0;
}

一般向普通文件寫入是全緩沖。此時程序還沒結束,文件被關閉了,而我們的內容還在FILE內緩沖區中,沒有刷新到文件內核緩沖區中,所以沒有顯示打印內容。

那為什么fflush刷新呢?想必底層是調用write系統調用,實際上就是如此。

fflush底層調用write系統調用,將FILE內緩沖區強制刷新到文件內核緩沖區中,文件內核緩沖區在刷新出來給我們看到。(由語言到內核的過程)

擴展

int main()
{//C提供const char *s1 = "hello printf\n";printf(s1);const char *s2 = "hello fprintf\n";fprintf(stdout,s2);const char *s3 = "hello fwrite\n";fwrite(s3,strlen(s3),1,stdout);//系統const char *s4 = "hello write[syscall]\n";write(1,s4,strlen(s4));//創建子進程fork();return 0;
}

在上面這段程序當中, 執行過程是:調用printf、fprintf、fwrite三個C庫函數和系統調用write,再創建一個子進程,我們來觀察兩種情況:

情況1:向顯示器進行寫入 --> 刷新策略:行刷新

由于是行刷新策略,當遇到換?符時語言級緩沖區會自動刷新到文件內核緩沖區中,因此是有規律地刷新到文件內核緩沖區中,此時創建子進程,之后程序結束需要情況語言級緩沖區,但緩沖區上并沒有數據。最終文件內核緩沖區再刷新出去,向顯示器寫入數據,給我們看到符合我們需求的結果。

情況2:向文件進行寫入 --> 刷新策略:全緩沖

但當向文件進行寫入時,打印結果并不是符合我們所想的,為什么同樣的代碼會有兩種不同的結果呢?

首先,向文件進行寫入一般的刷新策略是全緩沖,前三個都是C語言提供的庫函數,因此都是向FILE內緩沖區進行寫入,而write是系統調用,寫到文件內核緩沖區中,因此先看到write[syscall]這個結果并不例外。

但是為什么有兩段同樣的打印結果呢?

write結束后,此時會調用fork創建子進程,之后程序就結束了。由于程序結束了,就需要清空緩沖區,而此時我們是父子進程啊,而子進程清空緩沖區實際上就是修改數據啊,修改就要發生寫時拷貝,并不會影響父進程的緩沖區。父進程再進行清空緩沖區,呈現出刷新兩次的結果。(即父子進程各自執行退出邏輯,刷新自己的緩沖區)

cout和cerr分開

將文件內核緩沖區刷新到磁盤

int fsync(int fd);

強制將指定文件描述符(fd)對應的文件的所有已修改數據和元數據從操作系統緩存(如頁緩存)刷新到磁盤。

語言級緩沖區的意義

  • IO相關函數與系統調?接?對應,并且庫函數封裝系統調?,所以本質上,訪問?件都是通過fd訪問的,而調用系統調用是有成本的(浪費時間)。
  • 就拿C語言的malloc來說,我們在造vector輪子時,說過擴容盡量往1.5倍和2倍去靠,是為了減少擴容次數,頻繁擴容只是原因之一,并不是重點。malloc底層調用了系統調用,這意味著會頻繁調用系統調用,要花費操作系統的時間。
  • C語言為什么要提供緩沖區呢?FILE結構體內的緩沖區允許積壓,加速IO函數的調用頻率。而使用C語言IO接口,提高了效率,進而使單位時間內,執行C代碼行數就變多了,從而也反向提高了IO接口的效率。

總結

本文深入探討了Linux系統中緩沖區的設計原理與實現機制。文章從Linux"一切皆文件"的哲學出發,分析了緩沖區作為銜接用戶進程與底層設備的關鍵中間層,如何通過減少系統調用次數、平衡CPU與低速I/O設備的速度差異來提升系統性能。內容涵蓋緩沖區的三種類型(全緩沖、行緩沖、無緩沖)、FILE結構體實現原理,并通過具體代碼示例演示了不同緩沖策略下的I/O行為差異。文章特別解析了C語言緩沖區與內核緩沖區的交互機制,包括緩沖區刷新時機、父子進程間的緩沖區復制問題等,最終闡明了緩沖區設計在平衡統一接口與高效I/O訪問之間的重要意義。

?

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

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

相關文章

一、計算機的數據存儲

計算機的世界只有0和1。 1.1 進制 十進制整數->二進制整數&#xff1a;除2倒取余二進制->十進制&#xff1a;權值相加法 結論&#xff1a;1位8進制值 3位二進制值&#xff0c;1位十六進制值 4位二進制值 public class JinZhiDemo {public static void main(String[]…

SpringBoot集成XXL-JOB保姆教程

第一步&#xff1a; 下載xxl-job源碼到本地&#xff0c;地址如下&#xff1a; xxl-job: 一個分布式任務調度平臺&#xff0c;其核心設計目標是開發迅速、學習簡單、輕量級、易擴展。現已開放源代碼并接入多家公司線上產品線&#xff0c;開箱即用。 第二步&#xff1a; 創建…

Debezium日常分享系列之:Debezium 3.2.2.Final發布

Debezium日常分享系列之&#xff1a;Debezium 3.2.2.Final發布Debezium CoreConnector啟動時出現難以理解的錯誤臨時阻塞快照失敗可能導致數據丟失的問題修復Debezium for OracleDebezium CoreConnector 啟動時出現難以理解的錯誤 我們解決了一個問題&#xff0c;即連接器會因…

Zoom AI 技術架構研究:聯合式方法與多模態集成

一、研究背景與概述 在當今數字化轉型加速的背景下,人工智能技術正深刻改變企業協作與溝通方式。作為全球領先的視頻會議平臺,Zoom 已從單純的通信工具轉型為全面的生產力平臺,而其 AI 技術架構是這一轉變的核心驅動力。本報告將深入分析 Zoom 的 AI 技術架構,特別是其創新…

排序-快速排序 O(n log n)

快排&#xff1a;1、設定一個中間值 q[ lr >>1 ] , 讓左右區間來比較2、左邊通過 i 依次比較&#xff0c;如果比這個中間值小&#xff0c;就繼續 , 直到不符合3、右邊通過 j-- 依次比較&#xff0c;如果比這個中間值大&#xff0c;就繼續 &#xff0c;直到不符合4、兩邊…

【Proteus仿真】定時器控制系列仿真——LED小燈閃爍/流水燈/LED燈帶控制/LED小燈實現二進制

目錄 0案例視頻效果展示 0.1例子1&#xff1a;基于AT89C51單片機的定時器控制小燈閃爍 0.2例子2&#xff1a;基于AT89C51單片機的定時器T0流水燈 0.3例子3&#xff1a;基于AT89C51單片機的定時器控制LED燈帶 0.4例子4&#xff1a;基于AT89C51單片機的定時器控制LED閃爍 0…

進階向:密碼生成與管理工具

密碼生成與管理工具&#xff1a;從零開始的完全指南在現代數字生活中&#xff0c;密碼是保護個人信息和賬戶安全的第一道防線。隨著網絡服務的普及&#xff0c;每個人平均需要管理數十個不同賬戶的密碼。一個強大且獨特的密碼通常應包含12個以上字符&#xff0c;混合大小寫字母…

解決 Gitee 中 git push 因郵箱隱私設置導致的失敗問題

解決 Gitee 中 git push 因郵箱隱私設置導致的失敗問題 在使用 Git 向 Gitee 遠程倉庫推送代碼時&#xff0c;可能會遇到因郵箱隱私設置引發的 git push 失敗情況。最近我就碰到了&#xff0c;現在把問題現象、原因和解決方法分享出來。 一、錯誤現象 執行 git push -u origin …

Flutter的三棵樹

“三棵樹”是 Flutter 渲染和構建UI的核心機制&#xff0c;理解它們對于掌握 Flutter 至關重要。這三棵樹分別是&#xff1a; Widget 樹 Element 樹 RenderObject 樹 它們協同工作&#xff0c;以實現 Flutter 的高性能渲染和高效的響應式編程模型。 Flutter 是聲明式的UI&…

同一臺nginx中配置多個前端項目的三種方式

目錄 第一種方式:配置多個二級域名 第二種方式:配置端口轉發(不推薦) 第三種方式:同一個server中基于location配置(重點講解) 第一種方式:配置多個二級域名 一個域名下面申請多個二級域名,每個二級域名配置一個vue前端項目,這個很好配置,在這里不再詳細說明。 …

第二家公司雖然用PowerBI ,可能更適合用以前的QuickBI

第二家公司雖然用PowerBI &#xff0c;可能更適合用以前的QuickBI現在回想一下&#xff0c;第二家公司數據源是MySQL &#xff0c;常規報表是用excel報表&#xff0c;另外還做了一張能發布到web的看板供運營使用。基于基本情況&#xff0c;quickbi 的早期版本是合適的&#xff…

STM32 USBx Device HID standalone 移植示例 LAT1466

關鍵字&#xff1a;USBx&#xff0c; Device, HID&#xff0c;standalone 1.設計目的 目前 USBx Device standalone 的官方示例較少&#xff0c;不過使用 STM32CubeMX 可以快速地生成 USBx Device 相關類的示例工程&#xff0c;會很方便大家的開發。這里以 NUCLEO-H563 為例&…

python創建并寫入excel文件

大家好&#xff0c;這里是七七&#xff0c;今天來跟大家分享一個python創建并寫入一個excel文件的小例子&#xff0c;話不多說&#xff0c;開始介紹。首先我們來看一下這一小段代碼。import openpyxl# 創建一個新的 Excel 工作簿workbook openpyxl.Workbook()# 獲取當前活動的…

react native 出現 FATAL EXCEPTION: OkHttp Dispatcher

react native 出現 FATAL EXCEPTION: OkHttp Dispatcher 報錯信息FATAL EXCEPTION: OkHttp DispatcherProcess: , PID: 8868java.lang.NoSuchMethodError: No virtual method toString(Z)Ljava/lang/String; in class Lokhttp3/Cookie; or its super classes (declaration of o…

sentinel實現控制臺與nacos數據雙向綁定

有兩種方式可以實現&#xff1a;Springboot客戶端做相應配置&#xff08;推薦&#xff09;修改sentinel-dashboard的源碼一、Springboot客戶端做相應配置&#xff08;推薦&#xff09;1、添加依賴<dependency><groupId>com.alibaba.csp</groupId><artifac…

Kubernetes (k8s)

Kubernetes (k8s) 以下是一份 ?Kubernetes (k8s) 基礎使用教程&#xff0c;涵蓋從環境搭建到核心操作的完整流程&#xff0c;附詳細命令和示例&#xff1a; &#x1f680; ?一、環境準備&#xff08;3種方式&#xff09;?? ?1. 本地開發環境&#xff08;推薦&#xff09;?…

三打ANSYS HFSS

2. 激勵方式&#xff08;端口&#xff09;詳細對比分析在HFSS中&#xff0c;“激勵方式”和“端口”這兩個詞經常混用&#xff0c;但嚴格來說&#xff0c;“端口”是實現“激勵”的一種最主要的方式。端口類型工作原理適用情況優點缺點波端口 (Wave Port)默認首選。計算端口的固…

3.python——數據類型轉換

python的數據類型轉換分為兩種&#xff1a; 隱式轉換&#xff1a;自動完成 顯式轉換&#xff1a;用類型函數轉換 隱式轉換 # 自動轉為浮點數 num_int 123 num_flo 1.23num_new num_int num_flo顯式轉換 整型 x int(1) # x 輸出結果為 1 y int(2.8) # y 輸出結果為 2 z …

迅為RK3568開發板OpenHarmonyv3.2-Beta4版本測試-命令終端

將串口連接到開發板的調試串口&#xff0c;進入 OpenHarmony 系統后&#xff0c;會自動進入 OpenHarmony終端&#xff0c;如下圖所示&#xff1a;

【面試題】介紹一下BERT和GPT的訓練方式區別?

BERT(雙向編碼器): 預訓練任務: 掩碼語言模型(MLM):隨機掩蓋15%的token,其中: 80%替換為[MASK] 10%替換為隨機token 10%保持原樣 下一句預測(NSP):判斷兩個句子是否連續(后續版本已移除) 訓練特點: 使用雙向Transformer編碼器 同時利用左右上下文信息 適合理解類任…