【Linux】深入理解Linux文件系統:從C接口到內核設計哲學


文章目錄

  • 前言
  • 一、C語言中的文件接口
    • 1. 文件指針(句柄)FILE*
      • 以寫方式打開文件,若文件不存在會新建一個文件
      • W寫入方式,在==打開文件之前==都會將文件內容全部清空
      • 追加寫方式,其用法與寫方法一致,不同在于a方法可以在文件結尾寫入
  • 二、認識文件系統調用
    • Linux下的系統調用open()
      • 第一個參數為文件路徑
      • 第二個參數為操作文件的方式
      • 第三個可選參數是更改創建文件的默認權限:
  • 三、訪問文件的本質
  • 四、重定向與緩沖區
    • 自定義重定向系統調用接口dup2
  • 再談“一切皆文件”
    • 1. 外設設備與文件系統的關系
    • 2. 擴展思想:
  • 總結


前言

在計算機系統中,文件由內容數據和元數據屬性共同構成。文件的完整生命周期分為兩個階段:

文件狀態存儲位置管理方式
未打開文件磁盤存儲介質文件系統通過inode管理
已打開文件內存內核通過file結構體管理
  • 所有文件操作本質上都是進程與文件系統的交互
  • 打開文件需要將文件屬性加載到內存
  • 文件內容采用按需加載策略(延遲加載)

研究文件系統本質是研究進程和文件之間的關系(文件是由進程打開的);未打開的文件存在磁盤上(存儲介質),文件要被打開(屬性)必須先要加載到內存;


一、C語言中的文件接口

基本輸入輸出 stdio.h
訪問磁盤的過程稱之為IO的過程,

1. 文件指針(句柄)FILE*

//C標準庫通過FILE結構體封裝文件描述符FILE *fopen(const char *path, const char *mode)
// mode參數決定了你的訪問權限
mode說明特性
“w”寫模式(清空文件)文件不存在時創建
“a”追加模式保留原內容,末尾寫入
“r”讀寫模式文件必須存在

以寫方式打開文件,若文件不存在會新建一個文件

若沒有指定路徑,程序會在默認當前路徑下創建,當前路徑指的是進程的當前路徑(使用ls /proc/[pid] 查看到當前進程的cwd)。
在這里插入圖片描述
同樣的,修改當前進程的工作目錄就可以改變創建文件的默認路徑。

chdir("home/ys") //修改進程工作路徑為home/ys

W寫入方式,在打開文件之前都會將文件內容全部清空

在這里插入圖片描述

上一個程序疑問:strlen要不要+1?

我們知道寫入字符串時需要將\0也寫入,我們試驗之后發現文本中多了@^這樣的亂碼,推測這就是\0,只不過vim文本編輯器將其解釋成了亂碼符號。結論是strlen不需要+1,文件系統沒有規定字符串必須以\0結尾

追加寫方式,其用法與寫方法一致,不同在于a方法可以在文件結尾寫入

二、認識文件系統調用

c語言程序在啟動時,會默認打開三個標準輸入輸出流文件:

stdin:鍵盤設備
stdout:顯示器文件
stderr:顯示器文件

文件其實是在磁盤上的,由于磁盤是外部設備,訪問文件實際上是訪問磁盤這樣的硬件。不同的語言有不同的文件操作方式,但在底層用的是都是一樣的實現方式——都需要調用系統接口open、read、write。

庫函數(fopen,printf,fscanf等)訪問硬件設備一定會通過系統調用來訪問。

Linux下的系統調用open()

在這里插入圖片描述

第一個參數為文件路徑

  • 若pathname以路徑的方式給出,則當需要創建該文件時,就在pathname路徑下進行創建。
  • 若pathname以文件名的方式給出,則當需要創建該文件時,默認在當前路徑下進行創建。(注意當前路徑的含義)

第二個參數為操作文件的方式

方式含義
O_RDONLY以只讀的方式打開文件
O_WRNOLY以只寫的方式打開文件
O_APPEND以追加的方式打開文件
O_RDWR以讀寫的方式打開文件
O_CREAT當目標文件不存在時,創建文件

1. O_WRONLY是寫方式,但是它并不會新建文件
2. O_CREAT打開文件時清空文件

3. O_APPEND 追加寫選項
寫入:

const char* message = "hello";
write(fd,message,strlen(message));
//write并不會對文件進行清空式寫入。
int fd = open("log.txt",O_WRONLY|O_CREAT|O_APPEND,0666); //追加
write(fd,message,strlen(message),);

第三個可選參數是更改創建文件的默認權限:

//eg:
int fd = open("log.txt",O_WRONLY|O_CREAT); 

創建權限錯誤,所以新建文件時需要告訴接口權限是什么。
在這里插入圖片描述

int fd = open("log.txt",O_WRONLY|O_CREAT,0666); 

在這里插入圖片描述
這里創建出來的并不是666而是664,應該要想到之前學到的權限掩碼(0002)的知識!

比特位級別的傳參方式原理:
使用位圖的方式,一次向操作系統傳遞多個標志位

三、訪問文件的本質

可以將其類比系統管理進程(struct_task),Linux系統中一切皆文件,因此管理進程勢必要通過先描述再組織的方法進行。要描述一個被打開的文件(struct_file),往往需要包含文件路徑、文件基本屬性(權限、大小、讀寫位置、訪問用戶的信息等)、文件的內核緩沖區信息、下一個struct_file的指針

一個進程可能會打開多個文件,那么進程與文件之間又是如何關聯的?(1:n)

進程PCB中會存在一個結構體指針struct files_struct *files指向了一個結構體,該結構體存放了一個存放各種文件PCB指針的數組;因為是數組,所以這也解釋了為什么open接口返回的是int類型的值了,進程根據這個下標就可以訪問對應文件。

如果嘗試打印一下返回值,發現文件描述符默認是從3開始的,那么0,1,2是什么文件呢?那就是標準輸入輸出錯誤流了!(stdin \ stdout \stderr

int fd = open("demo.txt",O_WRONLY |O_CREAT,0666);
cout << fd << endl; //3cout << stdin->_fileno << endl;//0
cout << stdout->_fileno << endl;//1
cout << stderr->_fileno << endl;//2

在這里插入圖片描述
既然一切皆文件,那么輸出流也是文件,因此我們可以使用以下代碼向標準輸出流文件中寫入message信息:

const char* message = "hello";
write(1,message,strlen(message));// 1 就是標準輸出流stdout

從標準輸入流文件中讀取buffer大小的字符放在buffer[1024]數組中 :

char buffer[1024];
read(0,buffer,sizeof(buffer));
printf("echo: %s\n",buffer);

四、重定向與緩沖區

文件描述符對應的分配規則是什么?

從0下標開始,尋找沒有被使用的數組位置,它的下標就是新文件的文件描述符值。

假設我們有一個空文件log.txt,有如下代碼,含義是將msg中的strlen長度的數據輸出到顯示器。

const char* msg = "hello linux\n";
write(1,msg,strlen(msg));

但如果先關閉了1描述符(即關閉標準輸出流),除了顯示器無法顯示外

close(1);
int fd = open("log.txt", O_RDONLY | O_CREAT, 0666);//1
const char* msg = "hello linux\n";
write(1,msg,strlen(msg));//此時寫入的就是1號文件描述符,即log.txt 文件

log.txt中居然存有數據。
這一工作,稱為輸出重定向。根據上面的知識可以意識到關閉了1描述符后,那么這里就是空著的,當使用open接口新建log.txt時,根據文件描述符分配規則,自然1號位就成為了log.txt的fd描述符,所以將本來要寫入stdout的數據寫入到了log.txt中。

自定義重定向系統調用接口dup2

int dup2(int oldfd,int newfd)
把oldfd復制到newfd
//oldfd 相當于 原本的 3 描述符
//newfd 相當于 原本的 1 描述符int fd = open("log.txt", O_WRONLY|O_CREAT, 0666);
dup2(fd, 1); 

這里要注意的是,重定向中的拷貝,不是將文件描述符表中的下標進行拷貝,而是對下標處的內容(文件結構體指針)進行拷貝!

使用dup2在打開文件log.txt后,進行了輸出重定向,將原本輸出到顯示器的內容寫入到了log.txt文件中。再次更改代碼open的宏參數(O_TRUNC -> O_APPEND),就成為了追加重定向操作。結果如下所示:
在這里插入圖片描述
同樣的,可以修改代碼讓其重定向標準輸入流至文件(默認read從stdin文件讀數據,重定向后,從log.txt文件中讀)。這一過程稱為輸入重定向

在這里插入圖片描述

以上是使用dup2重定向系統調用函數write、read,前面提到c語言printf、fprintf底層也是這樣的文件描述符表的結構,那是否可以控制c語言中的輸入輸出呢?

dup2(fd,1);
printf("hello printf\n");
fprintf(stdout,"hello printf\n");

回想之前的章節介紹到echo指令,可以進行輸出重定向,cat指令可以進行輸入重定向

echo "hello" > log.txt   //輸出重定向
cat < log.txt            //輸入重定向
echo "hello" >> log.txt  //追加重定向

進程的替換不會影響文件的訪問(包括重定向操作)——復習進程替換

stdout與stderr都是可以向顯示器打印,為什么要有2?他們倆的區別是什么?

有如下代碼,表示將字符串分別輸出到1(標準輸出流)和2(標準錯誤流)中。
在這里插入圖片描述

$ ./mytest 1>normal.log 2>err.log
//將stdout的數據重定向至normal.log
//將stderr的數據重定向至err.log

在這里插入圖片描述

實際上,1和2是相同的實現方式,只不過在使用中,相較于正常結果而言,更關注的是它的錯誤信息,而正常運行的信息往往很多,不便錯誤的篩查與糾正。因此,為了將錯誤信息分離出來,才有了標準錯誤流。

一個衍生用法:

$ ./mytest >normal.log 2>&1

再談“一切皆文件”

1. 外設設備與文件系統的關系

在這之前我們知道:所有操作計算機的動作都是由進程執行的,包括文件的訪問,每一種外設都要有描述他們的結構體對象(struct_dev)

此外,每一種外設都有其相獨特的讀寫方法,縱然每個外設對應的訪問實現方式不同(各家外設設備驅動的不同),而對于操作系統來看,這些外設無非都是一些需要進行讀寫的文件,而能夠直接進行文件訪問讀寫的就是進程(open接口),打開新的文件就會創建一個新的struct_file,這個結構體是不是很熟悉?在這個結構體中,就存在著能夠指向該文件具體實現自身讀寫行為的指針(struct fils_operations*),例如(指向了不同磁盤的讀寫方法,不同鍵盤的讀寫方法)。

  • 在Linux中,將struct_file這一層的邏輯關系稱為虛擬文件系統(VFS)

外設差異化被封裝在驅動中:不同廠商的驅動實現自己的讀寫邏輯(如razer_keyboard_readlogitech_keyboard_read),但必須遵循操作系統定義的接口。
?操作系統通過抽象層統一接口:上層應用只需調用 read()、write() 等標準接口,無需關心底層是羅技還是雷蛇設備。

2. 擴展思想:

這種設計模式與 面向對象編程中的多態性高度相似

?基類(抽象接口) ?:操作系統定義的設備驅動接口(如file_operations)。
?派生類(具體實現) ?:廠商驅動的讀寫函數(如雷蛇、羅技的實現)。
運行時多態 :通過函數指針動態綁定到具體實現。

通過這種機制,操作系統實現了外設的 ??“高內聚、低耦合”?,使得硬件廠商可以自由創新,同時保持軟件生態的兼容性。


總結

👍 ?感謝各位大佬觀看。如果本文有幫助,請點贊收藏支持~

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

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

相關文章

國產品牌芯洲科技100V降壓芯片系列

SCT2A25采用帶集成環路補償的恒導通時間(COT)模式控制&#xff0c;大大簡化了轉換器的片外配置。SCT2A25具有典型的140uA低靜態電流&#xff0c;采用脈沖頻率調制(PFM)模式&#xff0c;它使轉換器在輕載或空載條件下實現高轉換效率。 芯洲科技100V降壓芯片系列提供豐富的48V系…

ctfshow-大賽原題-web702

因為該題沒有理解到位&#xff0c;導致看wp也一直出錯&#xff0c;特此反思一下。 參考yu22x師傅的文章 &#xff1a;CTFSHOW大賽原題篇(web696-web710)_ctfshow 大賽原題-CSDN博客 首先拿到題目&#xff1a; // www.zip 下載源碼 我們的思路就是包含一個css文件&#xff0c;…

LabVIEW技巧——獲取文件版本信息

獲取可執行文件&#xff08;exe&#xff09;版本信息的幾種方法 方法1. LabVIEW自帶函數 labview自帶了獲取文件版本號的VI&#xff0c;但是沒有開放到程序框圖的函數選板中&#xff0c;在該目錄下可以找到&#xff1a;...\LabVIEW 20xx\vi.lib\Platform\fileVersionInfo.llb…

三格電子——CAN 轉光纖(點對點)布線常見問題

1、CAN 布線 &#xff08;1&#xff09;H 接 H ,L 接 L &#xff08;2&#xff09;兩端設備掛 120 歐姆電阻 2、假如用點對點的 CAN 轉光纖現實遠程傳輸 &#xff08;1&#xff09;H 接 H ,L 接 L &#xff08;2&#xff09;光端機都掛 120 歐姆電阻 每個光端機掛的設備有一個加…

python進階: 深入了解調試利器 Pdb

Python是一種廣泛使用的編程語言&#xff0c;以其簡潔和可讀性著稱。在開發和調試過程中&#xff0c;遇到錯誤和問題是不可避免的。Python為此提供了一個強大的調試工具——Pdb&#xff08;Python Debugger&#xff09;。 Pdb是Python標準庫中自帶的調試器&#xff0c;可以幫助…

React 設計藝術:如何精確拆分組件接口,實現接口隔離原則

接口隔離原則 接口隔離原則&#xff08;Interface Segregation Principle&#xff0c;簡稱 ISP&#xff09;也是面向對象設計中的重要原則之一。它的核心思想是&#xff0c;一個類不應該依賴它不需要的接口。在 React 開發中&#xff0c;遵循接口隔離原則可以提高代碼的可維護性…

內部聊天軟件,BeeWorks-安全的企業內部通訊軟件

企業在享受數據便利的同時&#xff0c;如何保障企業數據安全已經成為無法回避的重要課題。BeeWorks作為一款專為企業設計的內部通訊軟件&#xff0c;通過全鏈路的安全能力升維&#xff0c;為企業提供了一個安全、高效、便捷的溝通協作平臺&#xff0c;全面保障企業數據安全。 …

【零基礎】基于 MATLAB + Gurobi + YALMIP 的優化建模與求解全流程指南

MATLAB Gurobi YALMIP 綜合優化教程&#xff08;進階&#xff09; 本教程系統介紹如何在 MATLAB 環境中使用 YALMIP 建模&#xff0c;并通過 Gurobi 求解器高效求解線性、整數及非線性優化問題。適用于工程、運營研究、能源系統等領域的高級優化建模需求。 一、工具概覽 1.…

Freertos----互斥量

一、為什么要使用互斥量&#xff1f; 我們想讓任務A、B都執行add_a函數&#xff0c;a的最終結果是18817。 假設任務A運行完代碼①&#xff0c;在執行代碼②之前被任務B搶占了&#xff1a;現在任務A的R0等于1。 任務B執行完add_a函數&#xff0c;a等于9。 任務A繼續運行&#…

高級java每日一道面試題-2025年4月11日-微服務篇[Nacos篇]-Nacos使用的數據庫及其數據同步機制是什么?

如果有遺漏,評論區告訴我進行補充 面試官: Nacos使用的數據庫及其數據同步機制是什么&#xff1f; 我回答: Nacos 使用的數據庫及其數據同步機制詳解 在微服務架構中&#xff0c;Nacos 作為服務注冊與配置管理的核心組件&#xff0c;其數據存儲和同步機制對系統的高可用性和…

揭秘大數據 | 22、軟件定義存儲

揭秘大數據 | 19、軟件定義的世界-CSDN博客 揭秘大數據 | 20、軟件定義數據中心-CSDN博客 揭秘大數據 | 21、軟件定義計算-CSDN博客 老規矩&#xff0c;先把這個小系列的前三篇奉上。今天書接上文&#xff0c;接著敘軟件定義存儲的那些事兒。 軟件定義存儲源于VMware公司于…

git常用修改命令

1. 代碼回退與歷史修改 git reset 模式命令示例作用范圍適用場景--softgit reset --soft HEAD~1僅移動 HEAD 指針重新提交之前的修改--mixedgit reset HEAD~1 (默認)重置暫存區取消已 add 但未提交的文件--hardgit reset --hard a1b2c3d徹底丟棄工作區和暫存區徹底回退到某個…

【ubuntu】linux開機自啟動

目錄 開機自啟動&#xff1a; /etc/rc.loacl system V 使用/etc/rc*.d/系統運行優先級 遇到的問題&#xff1a; 1. Linux 系統啟動階段概述 方法1&#xff1a;/etc/rc5.d/ 腳本延時日志 方法二&#xff1a;使用 udev 規則來觸發腳本執行 開機自啟動&#xff1a; /etc/…

Python深度學習基礎——深度神經網絡(DNN)(PyTorch)

張量 數組與張量 PyTorch 作為當前首屈一指的深度學習庫&#xff0c;其將 NumPy 數組的語法盡數吸收&#xff0c;作為自己處理張量的基本語法&#xff0c;且運算速度從使用 CPU 的數組進步到使用 GPU 的張量。 NumPy 和 PyTorch 的基礎語法幾乎一致&#xff0c;具體表現為&am…

光伏產品研發項目如何降本增效?8Manage 項目管理軟件在復合材料制造的應用

在復合材料制造領域&#xff0c;特別是光伏PECVD石墨舟和燃料電池石墨雙極板等高精尖產品的研發過程中&#xff0c;高效的項目管理直接決定了產品開發周期、質量和市場競爭力。然而&#xff0c;許多企業在項目立項、進度跟蹤、資源分配和質量控制等環節面臨挑戰。 針對這些痛點…

linux的glib庫使用

glib常用接口使用 1. glib介紹2. glib命令安裝3. 獲取glib的版本信息和兼容信息4. glib使用例子4.1 鏈表例子4.2 哈希表例子4.3 使用面向對象例子 1. glib介紹 廣泛應用于桌面環境、嵌入式系統、GNOME等項目中。它提供了完整的面向對象編程模型&#xff08;GObject&#xff09…

vs2022使用git方法

1、創建git 2、在cmd下執行 git push -f origin master &#xff0c;會把本地代碼全部推送到遠程&#xff0c;同時會覆蓋遠程代碼。 3、需要設置【Git全局設置】&#xff0c;修改的代碼才會顯示可以提交&#xff0c;否則是灰色的不能提交。 4、創建的分支&#xff0c;只要點擊…

SAP ECCS 標準報表 切換為EXCEL電子表格模式

在解決《SAP ECCS標準報表在報表中不存在特征CG細分期間 消息號 GK715報錯分析》問題過程中通過DEBUG方式參照測試環境補錄數據后&#xff0c;不再報GK715錯誤&#xff0c;此時用戶要的很急&#xff0c;要出季報。要求先把數據導出供其分析出季報。 采用導出列表方式&#xff…

基于 Python 和 OpenCV 技術的疲勞駕駛檢測系統(2.0 全新升級,附源碼)

大家好&#xff0c;我是徐師兄&#xff0c;一個有著7年大廠經驗的程序員&#xff0c;也是一名熱衷于分享干貨的技術愛好者。平時我在 CSDN、掘金、華為云、阿里云和 InfoQ 等平臺分享我的心得體會。 &#x1f345;文末獲取源碼聯系&#x1f345; 2025年最全的計算機軟件畢業設計…

MATLAB項目實戰(一)

題目&#xff1a; 某公司有6個建筑工地要開工&#xff0c;每個工地的位置&#xff08;用平面坐標系a&#xff0c;b表示&#xff0c;距離單位&#xff1a;km&#xff09;及水泥日用量d(t)由下表給出&#xff0e;目前有兩個臨時料場位于A(5,1)&#xff0c;B(2,7)&#xff0c;日儲…