【Linux取經路】文件系統之緩沖區

在這里插入圖片描述

文章目錄

  • 一、先看現象
  • 二、用戶緩沖區的引入
  • 三、用戶緩沖區的刷新策略
  • 四、為什么要有用戶緩沖區
  • 五、現象解釋
  • 六、結語

一、先看現象

#include <stdio.h>
#include <string.h>
#include <unistd.h>int main()
{const char* fstr = "Hello fwrite\n";const char* str = "Hello write\n";printf("Hello printf\n");fprintf(stdout, "Hello fprintf\n");fwrite(fstr, strlen(fstr), 1, stdout); // 返回值是寫入成功的快數write(1, str, strlen(str)); // 返回值是寫入成功的字節數// fork();return 0;
}

在這里插入圖片描述
結構分析:帶 fork 的輸出重定向最終把有一些內容向 log.txt 文件中寫入了多次,并且打印順序也有所不同。

int main()
{const char* fstr = "Hello fwrite";const char* str = "Hello write";printf("Hello printf");fprintf(stdout, "Hello fprintf");fwrite(fstr, strlen(fstr), 1, stdout); // 返回值是寫入成功的快數close(1);// write(1, str, strlen(str)); // 返回值是寫入成功的字節數// fork();return 0;
}

在這里插入圖片描述
結果分析:代碼中只使用了庫函數向顯示器中進行寫入,并且在字符串的結尾沒有加 \n,在最后面將標準輸出對應的文件描述符進行了關閉,最終顯示器上什么也沒有。上一段代碼在字符串的結尾加上了 \n 最終字符串被成功的打印到了屏幕上。

int main()
{const char* str = "Hello write";write(1, str, strlen(str)); // 返回值是寫入成功的字節數close(1);return 0;
}

在這里插入圖片描述
結果分析:字符串的結尾依然不加 \n,但是這一次采用系統調用接口,最后仍然將標準輸出對應的文件描述符進行關閉,這一次字符串被成功的打印了出來。

二、用戶緩沖區的引入

write 為什么能將不帶 \n 的字符串寫入到顯示器文件中。首先我們需要明確一點進程打開的每一個文件都有一個屬于自己的操作系統級別的文件緩沖區,該緩沖區的存在,可以減少對外設的讀寫操作以提高計算機的效率。舉個栗子,在一個進程中向磁盤里的同一個文件進多次行寫入,文件緩沖區的存在,可以將每次寫入的內容先存儲在文件緩沖區中,最后在程序退出或者調用 close 的時候,一次性將文件緩沖區中的所有內容刷新到磁盤。如果沒有該文件緩沖區,那在進程里對文件進行 n 次寫操做,就要對應 n 次向磁盤的寫操作,CPU 和外設之間是存在非常大的速度差的,這樣效率會非常低。

write 作為系統調用接口,它就是直接向文件緩沖區中寫入,最后在調用 close 接口或者程序退出的時候,會將文件緩沖區的內容刷新到對應的外設中。

printffprintffwrite 底層一定是封裝了 write 系統調用接口,那為什么使用 write 系統調用接口就可以將字符串寫入到顯示器,使用 C 庫函數沒能把字符串寫入到顯示器文件?原因在進度條的那篇文章中講過,我們使用的這些 C 庫函數,是把字符串寫入到了緩沖區中,這個緩沖區和上面的文件緩沖區有所不同,這里說的緩沖區是 C 語言給我們提供的語言層面的緩沖區,也叫做用戶級緩沖區\n 具有刷新用戶級緩沖區的作用,因此不加 \n 并且在程序結束前將顯示器對應的文件描述符進行了關閉,最終就導致字符串在用戶級緩沖區中,沒有被刷新到文件緩沖區,所以屏幕上就什么也沒有。這里我們可以肯定,在這些 C 庫函數中,并不是立即調用 write 接口,而是在遇到 \n 后才去調用 write 接口將用戶緩沖區的內容刷新到文件緩沖區中。

在這里插入圖片描述

總結:使用 C 系統調用接口向文件中寫入,寫入的內容先被存儲在用戶緩沖區中,在合適的時候(遇到 \n)才會進行刷新,這里刷新的本質是調用 write 將數據從用戶緩沖區寫入內核。

之前說的 exit 會刷新緩沖區,其實就是刷新用戶緩沖區,因為 exit 作為 C 庫函數,可以看見用戶緩沖區,而 _exit 作為系統調用接口,無法看到語言層面的用戶緩沖區,因此也就無法刷新用戶緩沖區。

三、用戶緩沖區的刷新策略

  • 無緩沖:直接刷新,數據不在用戶緩沖區中停留。

  • 行緩沖:不刷新,直到碰到 \n

  • 全緩沖:緩沖區滿了才刷新。

所謂刷新就是調用 write 接口將數據寫入操作系統中的文件緩沖區。顯示器文件對應采用的就是行緩沖,向磁盤文件中寫入采用的是全緩沖。進程在退出的時候也會刷新用戶緩沖區,還可以調用 fflush 進行刷新。

四、為什么要有用戶緩沖區

  • 解決效率問題,緩沖區就像菜鳥驛站,不需要我們自己坐火車坐飛機去送東西,而是直接交給菜鳥驛站,然后就可以干自己的事情了,菜鳥驛站可以選擇攢上一大批快遞然后統一寄送出去。用戶緩沖區的存在本質上提高了 C 語言的效率,也就是提高了用戶的效率,因為 C 語言是程序員在使用,在使用 C 庫函數進行文件寫入時,大部分情況只需要把數據交給緩沖區,然后就可以快速的返回,不需要每一次都親力親為的去和操作系統打交道。

  • 配合格式化,有些和文件寫入相關的 C 庫函數是格式化輸出函數,在我們看來,它可以寫入整形、符點型,但是最終都是以字符串的形式進行寫入。格式化就是將類型全都轉化成字符串,先寫入到用戶緩沖區,用戶緩沖區中存的一定都是字符串。

用戶緩沖區,有進也有出,將數據寫入到用戶緩沖區中就就叫做進,將用戶緩沖區中的數據刷新到內核中的文件緩沖區中,被刷新的數據就可以從用戶緩沖區中刪掉,這就叫做出。用戶緩沖就像就像水流一樣源源不斷,流的概念就是因此而來。

小TipsFILE 里面就有對應打開文件的緩沖區字段和維護信息。每個被進程打開文件都有自己對應的文件緩沖區。FILE 對象屬于用戶,用戶緩沖區可以看作是在堆上申請的一塊空間。

五、現象解釋

這下再來解釋上面代碼中有 fork 然后重定向,寫入了多次的原因。首先重定向后,將本來向顯示器文件寫入的內容,寫到了磁盤文件,顯示器文件的緩沖區采用行緩沖,即遇到 \n 就會刷新,而磁盤文件采用的是全緩沖,當緩沖區滿了才刷新。因此在重定向后,會把三條 C 庫函數寫入的內容全部保存到緩沖區中,然后調用 fork 創建子進程,此時父子進程代碼共享,數據寫時拷貝,在程序退出的時候回去刷新用戶緩沖區,上面說過,刷新就是將用戶緩沖區中的數據寫入到內核,然后將用戶緩沖區中的內容清空,上面還說過,緩沖區就是在堆上申請的一段空間,可以看作數據部分,因為要刪除數據,所以就會進行寫時拷貝,此時之前父進程用戶緩沖區中的內容就會給子進程拷貝一份,然后父子進程都執行刷新動作,各自刷新自己的緩沖區數據,這就是為什么最終出現多份的原因。沒有重定向,只向顯示器打印四條消息,是因為顯示器采用的是行刷新策略,在調用 fork 前,對應的字符串就已經被刷新出去了。在 fork 的時候,父進程的用戶緩沖區中是空的,什么也沒有。

磁盤文件全緩沖驗證

int main()
{const char* fstr = "Hello fwrite\n";const char* str = "Hello write\n";printf("Hello printf\n");sleep(2);fprintf(stdout, "Hello fprintf\n");sleep(2);fwrite(fstr, strlen(fstr), 1, stdout); // 返回值是寫入成功的快數sleep(2);write(1, str, strlen(str)); // 返回值是寫入成功的字節數sleep(5);fork();return 0;
}

在這里插入圖片描述
分析:最先將 write 內容寫入到文件中,因為它是直接寫入到文件緩沖區,而剩下的 C 庫函數對應的內容是統一一次全部刷新到內核,即使每個字符串后面都有 \n,但最后還是統一全部刷新,這就證明了磁盤文件采用的是全刷新策略。

六、結語

今天的分享到這里就結束啦!如果覺得文章還不錯的話,可以三連支持一下,春人的主頁還有很多有趣的文章,歡迎小伙伴們前去點評,您的支持就是春人前進的動力!

在這里插入圖片描述

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

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

相關文章

HW面試常見知識點(新手認識版)

shiro漏洞原理 shiro漏洞原理是攻擊者利用shiro的默認密鑰偽造cookie&#xff0c;觸發JAVA反序列化執行命令或者寫shell。 shiro工具原理 跑默認key shiro550和721的區別 721是需要有效的登錄才可以 550不用登錄就可以直接跑key log4j原理 log4j是一款通用日志記錄工具&#xf…

【思揚贈書 | 第3期】由面試題“Redis是否為單線程”引發的思考

?? 寫在前面參與規則&#xff01;&#xff01;&#xff01; ?參與方式&#xff1a;關注博主、點贊、收藏、評論&#xff0c;任意評論&#xff08;每人最多評論三次&#xff09; ??本次送書1~4本【取決于閱讀量&#xff0c;閱讀量越多&#xff0c;送的越多】 很多人都遇到…

設計模式-抽象工廠模式(C++)

抽象工廠模式是一種設計模式&#xff0c;它提供了一個接口來創建一系列相關或相互依賴的對象&#xff0c;而無需指定它們具體的類。下面是一個使用 C 實現抽象工廠模式的示例&#xff1a; // 抽象產品類 class AbstractProductA { public:virtual void DoSomething() 0; };cl…

gitlab的使用

前一篇文章我們已經知道Git人人都是中心&#xff0c;那他們怎么交互數據呢&#xff1f; ? 使用GitHub或者碼云等公共代碼倉庫 ? 使用GitLab私有倉庫 目錄 一、安裝配置gitlab 安裝 初始化 這里初始化完成以后需要記住一個初始密碼 查看狀態 二、使用瀏覽器訪問&#xf…

Math方法,以及三角函數計算

abs(x) 返回參數的絕對值 var xMath.abs(-5) //5floor(x) 向下舍入為最接近的整數。 var xMath.floor(2.1) //2ceil(x) 向上舍入為最接近的整數。 var xMath.ceil(2.1) //3fround(x) 最接近的&#xff08;32 位單精度&#xff09;浮點表示。 var xMath.fround(2.60) //2.59…

小凡爬樓梯

解法&#xff1a; dp[i]:到第i階梯&#xff0c;總共dp[i]種方案 狀態轉移方程&#xff1a; base condition: #include<iostream> #include<vector> #include<algorithm> using namespace std; #define endl \n int main() {vector<long long> dp(51…

js數據處理util

方法匯總 據時間范圍生成時間刻度數據 /**params startDate 開始時間*params endDate 結束時間*params timeUnit 時間間隔,注意是毫秒數**/function createTimeUnitListByTimeRange(startDate, endDate, timeUnit){let startSeconds new Date(startDate).getTime();let endS…

【前綴和】560. 和為 K 的子數組

560. 和為 K 的子數組 解題思路 創建一個前綴和數組 preSum&#xff0c;其長度比原數組 nums 多 1。preSum[i] 表示 nums 中前 i 個元素的和。通過遍歷 nums 數組&#xff0c;計算前綴和數組 preSum。 在嵌套的兩個循環中&#xff0c;對所有可能的子數組進行窮舉&#xff1a;…

板塊一 Servlet編程:第四節 HttpServletResponse對象全解與重定向 來自【湯米尼克的JAVAEE全套教程專欄】

板塊一 Servlet編程&#xff1a;第四節 HttpServletResponse對象全解與重定向 一、什么是HttpServletResponse二、響應數據的常用方法三、響應亂碼問題字符流亂碼字節流亂碼 四、重定向&#xff1a;sendRedirect請求轉發和重定向的區別 在上一節中&#xff0c;我們系統的學習了…

學習C++,你不能錯過這4個編程軟件

作為一門起源比較早的編程語言&#xff0c;C應用的范圍非常廣&#xff0c;編程軟件自然也非常多。今天小編給大家簡單介紹4個不錯的C編程軟件&#xff0c;感興趣的小伙伴可以去嘗試一下。 1、visual studio Microsoft visual studio community 15/17(一般簡稱vs)&#xff0c;…

jdwp-event command Set

Event Command Set (64) Composite (100) 事件命令集 (64) 復合命令 (100) 目標虛擬機中的給定時間可能會發生多個事件。 例如&#xff0c;給定位置可能有多個斷點請求&#xff0c;或者您可能單步執行到與斷點請求相同的位置。 這些事件作為復合事件一起傳遞。 為了統一&#x…

redis:數據傾斜是什么?怎么應對熱點數據?

要知道什么是數據傾斜就的搞清楚redis是怎么存儲和訪問數據的。數據會按照一定的規則分布到不同槽上&#xff0c;然后槽又落在不同的機器節點上。比如把key進行crc16函數計算后的值對槽取模&#xff0c;然后槽會分配到不同的節點上。然后存取都會到對應的節點上去進行處理。 傾…

黑色金屬冶煉5G智能工廠數字孿生可視化管控系統,推進金屬冶煉行業數字化轉型

黑色金屬冶煉5G智能工廠數字孿生可視化管控系統&#xff0c;推進金屬冶煉行業數字化轉型。隨著科技的不斷發展&#xff0c;數字化轉型已經成為各行各業發展的必然趨勢。金屬冶煉行業作為傳統工業的重要組成部分&#xff0c;也面臨著數字化轉型的挑戰和機遇。為了推進金屬冶煉行…

在 Windows 上使用 VC++ 編譯 OpenSSL 源碼的步驟

在 Windows 上使用 VC 編譯 OpenSSL 源碼的步驟如下&#xff1a; 準備工作 安裝 Visual Studio 2017 或更高版本。安裝 Perl 腳本解釋器。安裝 NASM 匯編器。 編譯步驟 下載 OpenSSL 源碼。解壓 OpenSSL 源碼。打開命令行工具&#xff0c;并進入 OpenSSL 源碼目錄。運行以下…

Sublime text 3 配置

1.下載 打開官網鏈接&#xff1a;Download - Sublime Text或者去百度軟件中心搜索sublimeText3&#xff08;根據自己的實際情況下載對應的版本&#xff09; 2.安裝&#xff1a; 雙擊上一步下載下來的“Sublime Text Build 3083 x64 Setup.exe”&#xff0c;記得選擇“Add to…

IT資訊——全速推進“AI+鴻蒙”戰略布局!

文章目錄 每日一句正能量前言堅持長期研發投入全速推進“AI鴻蒙”戰略 人才戰略新章落地持續加碼核心技術生態建設 后記 每日一句正能量 人總要咽下一些委屈&#xff0c;然后一字不提的擦干眼淚往前走&#xff0c;沒有人能像白紙一樣沒有故事&#xff0c;成長的代價就是失去原來…

2023 龍蜥操作系統大會演講實錄:《兼容龍蜥的云原生大模型數據計算系統——πDataCS》

本文主要分三部分內容&#xff1a;第一部分介紹拓數派公司&#xff0c;第二部分介紹 πDataCS 產品&#xff0c;最后介紹 πDataCS 與龍蜥在生態上的合作。 杭州拓數派科技發展有限公司&#xff08;簡稱“拓數派”&#xff0c;英文名稱“OpenPie”&#xff09;是國內基礎數據計…

論文發表 | 頂會頂刊的實驗是如何煉成的

前言:Hello大家好,我是小哥談。在計算機科學研究領域,尤其是當你追求頂級會議和期刊的發表時,沒有什么?實驗設計更關鍵了。為什么這么說?理由很簡單。實驗不僅僅是你?來 檢驗假設的?段,它更是審稿?會重點關注和閱讀的部分,也是你驗證??研究多么創新、多么重要的內…

0221 解決萬得導出數據excel無法python讀入的問題

報錯如下&#xff1a; TypeError: <class openpyxl.styles.named_styles._NamedCellStyle>.name should be <class str> but value is <class NoneType> 原因分析&#xff1a; 萬得導出的xlsx帶有某些格式&#xff0c;比如首行加粗&#xff0c;excel桌面端工…

Linux之用戶和用戶組

目錄 一、簡介 1.1 用戶賬號分類 二、用戶 2.1 useradd 2.2 userdel 2.3 usermod 2.4 passwd 2.5 su 2.6 登出 三、用戶組 3.1 groupadd 3.2 groupdel 3.3 groupmod 3.4 newgrp 四、用戶賬號系統 4.1 /ect/passwd 4.2 常見的偽用戶如下所示 五、思維導圖 …