Linux文件IO——緩沖區磁盤上的文件管理

前言

什么是緩沖區?

  • 緩沖區是內存空間上的一小段內存,我們平常在寫程序的時候,其實是很難感知到緩沖區的存在的,接下來看一段代碼,可以很好地體現緩沖區的存在。
#include<stdio.h>
#include<unistd.h>
int main()
{printf("這是我關于緩沖區的一次小測試");sleep(2);return 0;
}

運行后

789721af98364a54a4693dbab0234c5d.png

2秒后

0f855b0f451f468faf696a7b9d3b03a8.png

按照我們對于平常代碼執行順序的理解,我們應該先打印printf中的文本信息,在執行sleep函數休眠2秒,但實際并不是這樣,事實恰恰相反。

對上述代碼做一次改動,再次運行

#include<stdio.h>
#include<unistd.h>
int main()
{printf("這是我關于緩沖區的一次小測試\n");sleep(2);return 0;
}

我們添加在文本信息后添加了換行符"\n",

結果

8957a6a36be34eef91a0659c914b2fa0.png

? 這一次,符合我們心中的預期,先打印文本信息,在進行休眠。

? 這其實就是緩沖區在發揮作用,有人說,緩沖區讓我對代碼執行產生誤解,我摸不清代碼怎么執行了,為什么要存在它?

? 為什么會有緩沖區,緩沖區的作用是什么?這就是今天我們所探究的。

緩沖區

緩沖區是什么?

  • 緩沖區是內存空間上的一段內存,大小由操作系統分配。
  • 一個暫時存儲數據的區域?

緩沖區存在于哪里?

  • 緩沖區存在于內存空間

1.為什么要有緩沖區呢?

? 緩沖區是一個暫時存儲數據的地方,當數據足夠,就會向目標地點進行輸送,我們現實生活中也存在這樣的地方,就是我們日常的快遞站。

? 當你需要將禮物送給你的朋友,而你們相距甚遠的時候,比如一個在江西,一個在黑龍江,你會親自將禮物送到他手上嗎?

b7d0c19fd42f4da2a672f42128627b9d.jpeg

?

? 答案顯然是否定的,那樣成本不僅高,還會耗費你大量的時間,這時候,我們都會選擇——寄快遞,讓快遞員替我們將禮物送出,當你將快遞交給快遞站,他們也不會立刻將你的快遞發出,送一個快遞,對他們來說和讓你自己送沒有區別,不過耗費的不是你的時間和成本,快遞站也有他們自己的寄送規則。

快遞站寄送規則

  • 立即寄送
  • 等到一定數量寄送
  • 存滿快遞寄送

以上三種規則,第一種時間快,但成本太大,收益幾乎沒有,甚至倒貼;

第二種,不僅能有收益,還能保障效率;

第三種,犧牲時間換取收益,收益提高了,但用戶等待的時間很長。

快遞站的這三種寄快遞規則,也就相當于我們緩沖區的推送(刷新)數據規則

  1. 實時刷新
  2. 行刷新
  3. 存滿刷新

通過上述,我們理解的緩沖區的作用

  • 提高效率,暫時存儲數據,待到數據數量達到一定時,刷新推送數據到磁盤文件當中,避免了多次讀寫帶來的時間損耗,降低效率? ? ? ? ? ? ? ? ? ? ? ? ?

緩沖區的運作過程

? ?當一個文件被進程打開的時候,它就不存在于磁盤之上,而是存在于內存之上,這是因為操作系統會將其屬性和內容均加載到內存上,屬性我們知道,操作系統內核會形成一個struct file類型的結構體來保存屬性。

那文件內容呢?

  • ? 被操作系統拷貝到緩沖區當中,內核文件管理不僅會創建文件結構體,還會為文件內容分配一段內存空間,也就是我們的緩沖區,將文件內容放入其中,并為文件結構體添加相關指針,用來管理這一段內存空間。526bfb9dec18496eb0379b538c8d4134.jpeg
  • ? 當進程對文件內容進行操作的時候,實際并不是對磁盤上的文件進行操作,而是對內存上的文件進行操作,修改的,增加的,或者刪除的內容,都是在對內存上緩沖區里面的內容操作,當操作結束,關閉文件,結束進程,會自動刷新緩沖區,將緩沖區內的內容拷貝到磁盤當中。cd0f3563968d4daab67e2164acfc1d93.jpeg

? 所以,緩沖區的運作過程

  1. 文件被打開,文件內容從磁盤上拷貝到緩沖區當中
  2. 用戶進程對文件內容操作,實際是對內存中緩沖區中的內容操作
  3. 關閉文件,用戶進程結束,緩沖區刷新,其中的數據被重新拷貝到磁盤文件中。

總結:對文件內容的操作,本質就是文件內容數據的來回拷貝。

緩沖區的刷新規則

通過上面的學習

我們知道緩沖區的刷新的三個規則

  • 即時刷新
  • 行刷新
  • 全滿刷新

在我們開頭的代碼中,為什么加換行符之前不刷新,加了換行符后立刻刷新呢?

除了等待進程退出,自動刷新緩沖區

我們還有兩種方式刷新緩沖區

  • 使用換行符號強制刷新
  • 使用flush函數強制刷新

什么時候行刷新,什么時候全滿刷新呢?

  • 一般對于顯示器文件,我們采用行刷新的策略
  • 對于磁盤文件,我們一般全滿刷新。

來看一個樣例

#include<stdio.h>
#include<unistd.h>
#include<string.h>
int main()
{fprintf(stdout,"C: hello fprintf\n");printf("C: hello printf\n");fputs("C: hello fputs\n",stdout);const char*str="system call:hello write";write(1,str,strlen(str));fork();return 0;
}

當我們向顯示器當中打印的時候

結果:

0b754e69ef2f47d5bb5aa348cd1f431b.png

當我們向文件當中打印的時候

結果:

0c4dff89d9a844478c4b29211f8848bd.png

這是為何?

  1. 當我們向顯示器中打印信息的時候,為了照顧用戶體驗,顯示器的刷新方式默認為行刷新,并且每個打印語句都有\n,當執行到fork的時候,這時候的緩沖區已經被刷新完了,緩沖區當中已經沒有數據了。
  2. 當我們重定向到文件中的時候,刷新方式變成了全緩沖,即等到緩沖區數據到達一定體量再刷新數據,切刷新方式為全緩沖的時候,緩沖區也會變大,這時候代碼執行到fork語句的時候,緩沖區內還有數據。
  3. 子進程和父進程共享代碼和數據,如果執行到fork函數的時候,父進程緩沖區中還有數據,子進程也會一并將其繼承下來。


? 所以,當父子進程都退出的時候,父進程緩沖區的數據被刷新,子進程緩沖區的數據被刷新,子進程緩沖區的數據是繼承父進程的,所以會重復打印文本信息。

C式緩沖區和內核緩沖區

?當我們重定向,將信息輸出到指定文件中的時候

acbcc618e79e4666adec2fcc0a18b759.png

? 我們可以發現,C語言函數接口的語句,每個都打印了兩次,而系統接口函數的文本信息僅僅只打印了一次,這是為什么?

  • ? ? ?利用子進程會繼承父進程數據的特點,我們也可以讓子進程繼承緩沖區中的數據,實現兩次打印的效果,但這都是針對C語言函數接口而言的,對于系統函數接口write,它寫入的文本信息其實并不在父進程的緩沖區當中,而是在內核緩沖區,直接和操作系統打交道的緩沖區,而父進程和子進程的緩沖區,則是C語言自己維護的緩沖區。
  • ? ? 也就是說,此時共有三個緩沖區,父進程一個C語言緩沖區,子進程一個C語言緩沖區,一個內核緩沖區,父子進程的緩沖區中的數據是一樣的,重復的,內核緩沖區的數據則是系統調用接口write寫入的,獨一份的,當進程退出的時候,父子進程緩沖區數據被刷入內核緩沖區,再和內核緩沖區的數據一同被刷新拷貝到磁盤文件當中。44368a9d0965483e916b0eedb0569866.jpeg

? 緩沖區的作用就是提高效率,解決各個設備讀寫速度不匹配帶來的低速拖累高速的情況,C式緩沖區存在的目的也是為了減少用戶進程與內核緩沖區的交互,希望數據積攢到一定體量再寫入內核緩沖區,就像內核緩沖區和磁盤文件的存儲關系一樣。

? 如果想直接寫入內核緩沖區,就使用操作系統提供的系統調用接口,它們會直接對內核緩沖區讀寫,如read和write;如果想寫入C式緩沖區,則使用C語言提供的調用接口,如printf和scanf。


總結

  • 無論是C式緩沖區還是內核緩沖區,它們的存在目的都是為了避免頻繁讀寫,從而提高整體效率
  • 使用C調用接口,讀寫經過C式緩沖區;使用系統調用接口,讀寫跳過C式緩沖區,直接進入內核緩沖區。

磁盤上的文件管理

文件分為被打開、未被打開兩種狀態,被打開的文件加載到內存空間當中,由內核操作系統的文件系統同一管理,那未被打開的文件呢?

  • 未被打開的文件存在于磁盤之上,由磁盤的文件系統管理?

接下來,進入磁盤的文件管理學習!

?磁盤物理結構

首先來認識一下,磁盤

e8d27ce60f004b2f90f492817fccb039.png

磁盤由許多盤片組成,每個盤片由有正反兩個面,每個面分配一個磁頭,也就是說,有多少個盤面,就有多少個磁頭。

俯瞰盤片

  • 盤片分成許多空心圓,每個空心圓的邊緣都是一圈磁道。
  • 磁道平均分,每一段磁道就是扇區,扇區是磁盤存儲數據的基本單位。
  • 越內的扇區越小,但存儲的數據量不變,因此,它的數據密度更大。

注:扇區是磁盤存儲數據的基本單位,大小一般為512個字節。

?CHS方法

  1. 定位磁道
  2. 定位盤片
  3. 定位扇區

磁盤邏輯結構

如圖,將卷成圓形的磁帶抽出來,就是一條長帶。

同樣,我們也可以將磁盤看成卷起來的磁帶,將其扯出來,也是一條長帶。

這是一種線性結構,因此,我們可以將磁盤看成一個數組,一個元素就是一個扇區,對文件的查找,就可以轉換成對數組元素的查找!


磁盤的大小很大,而一個扇區的大小僅僅只有512字節,如果真正按照以扇區作為一個數組元素的方式劃分,那么數組元素的數量將達到恐怖的數字,這個數量也是我們不愿意看到的。

借用現實生活中,我們分省分市的劃分方法,為了更好地管理文件,磁盤管理系統對文件采用分區分組的管理方式。

先將磁盤分成幾個大區,再在一個大區中分組,最后組中存儲文件。

組中又分為幾個塊,這些塊就是磁盤文件管理的核心,我們認識一下

  • Data blocks:存儲文件內容的塊區,該塊區中有許多數據塊,數據塊用來存儲對應文件的內容,每個數據塊大小為4KB(8個扇區)
  • inode Table:存儲的是struct inode結構體,每個結構體大小為128字節,該結構體中保存的是文件的inode編號,屬性,數據塊指針(指向Data blocks中的數據塊,找到文件內容)
  • inode Bitmap:文件位圖,來判斷一個文件是不是存在,如果該文件存在,對應比特位為1,否則為0。
  • Block Bitmap:用來判斷對應的數據塊是否被占用,如果被占用,對應比特位為1,否則為0.
  • Group Descriptor Table:存儲的是當前組的信息,如屬性,數據塊使用量,文件量,組大小。
  • Super Block:存放文件系統本身的結構信息。記錄的信息主要有:大區中block和inode的總量,未使用的block和inode的量,一個block和inode的大小,最近一次掛載的時間,最近一次寫入數據的時間,最近一次檢驗磁盤的時間等其他文件系統的相關信息,Super Block的信息被破壞,該大區的文件系統結構就被破壞了
  • 一般一個分區中,有好幾個組都有Super Block,防止分區文件系統結構被破壞后無法被修復。

在組中,我們不止一次見到了inode。那么inode是什么?

  • inode是文件編號,一個inode代表一個文件,它是文件的唯一標識。

inode雖然是文件的唯一標識,但我們在平常Linux文件操作中,卻從未見過,一般都是靠文件名來查找,操作文件,這是為什么?

  • inode是文件的唯一標識符,這是對內核而言的,內核通過inode對文件進行定位。
  • 當文件被創建的時候,內核會為其分配inode,并為其文件名和inode建立映射關系,這個映射關系會被存儲到該文件的目錄文件當中。
  • 當用戶層使文件名操作文件,內核操作系統會先在該目錄下找到對應目錄關系,用inode替換文件名,再進行后續操作。

Linux操作系統怎么在磁盤中查找文件?

  • 依靠文件路徑查找文件,對于一個文件而言,文件路徑是唯一的。

原理:

  • 文件系統管理分區首先要進行掛載才能被使用,掛載點一般是一個目錄,后序將該目錄當成入口訪問某個分區上的文件。
  • 查找文件,依靠的是文件路徑,通過目錄進入分區,在通過文件名和inode的映射關系找到文件。

刪除或者修改一個文件的本質是什么?

  • 刪除一個文件的本質,就是將其inode Bitmap中的比特位置為0,將其Block?Bitmap中的比特位置0,這樣在邏輯上,該文件就被刪除了,當該inode被重新使用,數據塊內容被覆蓋,才是真正被刪除。這也為恢復文件提供機會,前提是保存了inode。
  • 修改一個文件的本質是通過struct inode結構體中的數據塊指針找到對應的數據塊,修改數據塊中的內容。

創建一個文件的過程是怎么樣的?

  • 權限檢查:
    • 在創建文件之前,內核會檢查當前用戶是否有在目標目錄中創建文件的權限。
    • 這通常涉及讀取目錄的權限位和當前用戶的權限。
  • 分配inode和數據塊:
    • 如果權限檢查通過,文件系統會為新文件分配一個inode,用于存儲文件的屬性(如文件大小、權限、時間戳等)。
    • 同時,文件系統還會為新文件分配必要的數據塊,用于存儲文件內容。
  • 更新目錄結構
    • 文件系統會在目標目錄中為新文件創建一個目錄項,將文件名與inode關聯起來。
    • 這通常涉及更新目錄的數據塊,以包含新文件的條目。

寄語

? ?本次文章介紹了緩沖區和磁盤上的文件管理,希望其中的一些觀點能夠幫助大家學習,緩沖區我個人認為難度較大,難以理解,其中有問題的地方希望大家能夠指出來,在評論區進行討論。

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

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

相關文章

Java中如何去自定義一個類加載器

之前寫過一篇&#xff0c;關于 類加載器和雙親委派的文章&#xff0c;里邊提到過可以根據自己的需要&#xff0c;去寫一個自定義的類加載器&#xff0c;正好有人問這個問題&#xff0c;今天有時間就來手寫一個自定義的類加載器&#xff0c;并使用這個自定義的類加載器來加載一個…

X86 RouterOS 7.18 設置筆記六:端口映射(IPv4、IPv6)及回流問題

X86 j4125 4網口小主機折騰筆記五&#xff1a;PVE安裝ROS RouterOS X86 RouterOS 7.18 設置筆記一&#xff1a;基礎設置 X86 RouterOS 7.18 設置筆記二&#xff1a;網絡基礎設置(IPV4) X86 RouterOS 7.18 設置筆記三&#xff1a;防火墻設置(IPV4) X86 RouterOS 7.18 設置筆記四…

代碼隨想錄|二叉樹|21合并二叉樹

leetcode:617. 合并二叉樹 - 力扣&#xff08;LeetCode&#xff09; 題目 給定兩個二叉樹&#xff0c;想象當你將它們中的一個覆蓋到另一個上時&#xff0c;兩個二叉樹的一些節點便會重疊。 你需要將他們合并為一個新的二叉樹。合并的規則是如果兩個節點重疊&#xff0c;那么…

LDR6500在Type-C轉DP視頻雙向互傳方案

LDR6500在Type-C轉DP視頻雙向互傳方案中扮演著核心角色&#xff0c;以下是對該方案的詳細解析&#xff1a; 一、LDR6500芯片概述 LDR6500是樂得瑞科技針對USB Type-C標準中的Bridge設備而開發的USB-C DRP&#xff08;Dual Role Port&#xff0c;雙角色端口&#xff09;接口USB…

Vue3中 ref 與 reactive區別

ref 用途: ref 通常用于創建一個響應式的基本類型數據&#xff08;如 string、number、boolean 等&#xff09;&#xff0c;但它也可以用于對象或數組 返回值: ref 返回一個帶有 .value 屬性的對象&#xff0c;訪問或修改數據需要通過 .value 進行 使用場景: …

CRM企業客戶關系管理系統產品原型方案

客戶關系管理系統&#xff08;CRM&#xff09;是企業產品應用中的典范&#xff0c;旨在通過信息技術和互聯網技術提升企業核心競爭力&#xff0c;優化企業與顧客在銷售、營銷和服務方面的互動。本作品提供了一套通用型的CRM系統原型模板&#xff0c;涵蓋數據管理、審批流程、統…

【算法】 【c++】字符串s1 中刪除所有 s2 中出現的字符

【算法】 【c】字符串s1 中刪除所有 s2 中出現的字符 eg&#xff1a; s1:“helloworld” s2:“wd” 刪除后&#xff1a;s1:“helloorl” 1 雙循環匹配并刪除–>時間復雜度O(n^2) string 里面的刪除函數–>erase std::string::erase 是 C 標準庫中用于刪除字符串中字符…

利用委托用戶控件、窗體之間傳值 c#

獲取數據方&#xff08;usercontrol111&#xff09;聲明 public Func<Tuple<int, int>> GetCurrentResult { get; set; }獲取數據方調用 var val GetCurrentResult?.Invoke() ?? new Tuple<decimal, decimal>(0, 0);數據發送方聲明與賦值 usercontrol111…

【3-14 STC-pair超級詳細的解說】

1. pair的定義和結構 ? 基礎概念&#xff1a;考察對std::pair模板類的理解&#xff0c;包括其頭文件&#xff08;<utility>&#xff09;和基本語法&#xff08;pair<T1, T2>&#xff09;。 ? 成員訪問&#xff1a;測試對first和second成員變量的使用能力。 ? 構…

機器人觸覺的意義

機器人觸覺的重要性 觸覺在機器人領域至關重要&#xff0c;尤其是在自主操作、精細操控、人機交互等方面。雖然視覺和語音技術已高度發展&#xff0c;但機器人在現實世界中的操作仍然受限&#xff0c;因為&#xff1a; 視覺有局限性&#xff1a;僅憑視覺&#xff0c;機器人難…

RabbitMQ消息持久化與Lazy模式對比分析

RabbitMQ消息持久化與Lazy模式對比分析 在RabbitMQ中&#xff0c;消息持久化與Lazy模式是兩種不同的機制&#xff0c;分別針對消息可靠性、存儲優化等不同維度設計。以下從六個層面進行深度對比&#xff1a; 一、核心目標與作用對象差異 維度消息持久化&#xff08;delivery_…

Search-R1 、 R1-Searcher 和 Search-O1

原文鏈接:https://i68.ltd/notes/posts/20250307-search-r1/ Search-R1 DeepSeek團隊開發的SEARCH-R1模型通過強化學習&#xff0c;讓AI學會了自主搜索信息并將其與推理過程無縫結合&#xff0c;性能提升高達26%高效、可擴展的RL訓練框架&#xff0c;用于推理和搜索引擎調用&…

linux 命令 tail

tail 是 Linux 中用于查看文件末尾內容的命令&#xff0c;常用于日志監控和大文件快速瀏覽。以下是其核心用法及常見選項&#xff1a; 基本語法 tail [選項] 文件名 常用選項 顯示末尾行數 -n <行數> 或 --lines<行數> 指定顯示文件的最后若干行&#xff08;…

某乎x-zse-96加密算法分析與還原

文章目錄 1. 寫在前面2. 接口分析3. 加密分析4. 算法實現 【&#x1f3e0;作者主頁】&#xff1a;吳秋霖 【&#x1f4bc;作者介紹】&#xff1a;擅長爬蟲與JS加密逆向分析&#xff01;Python領域優質創作者、CSDN博客專家、阿里云博客專家、華為云享專家。一路走來長期堅守并致…

Java常用算法

一、排序算法 排序算法是計算機科學中最基礎的算法之一&#xff0c;用于將一組數據按照特定順序排列。 1.1 冒泡排序&#xff08;Bubble Sort&#xff09; 通過重復遍歷列表&#xff0c;比較相鄰元素并交換位置&#xff0c;直到列表有序。時間復雜度&#xff1a;O(n)。 pub…

ubuntu 24 安裝 python3.x 教程

目錄 注意事項 一、安裝不同 Python 版本 1. 安裝依賴 2. 下載 Python 源碼 3. 解壓并編譯安裝 二、管理多個 Python 版本 1. 查看已安裝的 Python 版本 2. 配置環境變量 3. 使用 update-alternatives? 管理 Python 版本 三、使用虛擬環境為項目指定特定 Python 版本…

【后端】【django】Django 自帶的用戶系統與 RBAC 機制

Django 自帶的用戶系統與 RBAC 機制 Django 自帶的用戶系統&#xff08;django.contrib.auth&#xff09;提供了 身份驗證&#xff08;Authentication&#xff09; 和 權限管理&#xff08;Authorization&#xff09;&#xff0c;能夠快速實現 用戶管理、權限控制、管理員后臺…

怎樣使用Modbus轉Profinet網關連接USB轉485模擬從站配置案例

怎樣使用Modbus轉Profinet網關連接USB轉485模擬從站配置案例 Modbus轉profinet網關可以將Modbus協議轉化為profinet協議&#xff0c;以實現設備之間的數據交互。在實際使用過程中&#xff0c;我們需要使用Modbus協議進行設備通訊&#xff0c;而profinet協議則是用于工業自動化…

5.編譯鏈接和宏**

1. 宏&#xff08;考察很多&#xff09;-要求輕松實現宏&#xff0c;很容易出錯 #define 機制包括了一個規定&#xff0c;允許把參數替換到文本中&#xff0c;這種實現通常稱為宏或定義宏。 下面是宏的聲明方式&#xff1a; #define name(參數列表) 內容 參數列表的左括號必…

如何搭建一個適配微信小程序,h5,app的uni-app項目

在vscode搭建 uni-app 項目&#xff08;Vue 3 Vite Pinia uView Plus&#xff09; 一、環境準備 1. 安裝 Node.js 確保已安裝 Node.js&#xff08;需≥14版本&#xff09;&#xff0c;可通過以下命令檢查版本&#xff1a; node -v2. 安裝 VSCode 從 VSCode 官網 下載并…