Linux文件系統理解1

目錄

    • 一、初步理解系統層面的文件
      • 1. 文件操作的本質
      • 2. 進程管理文件核心思想
    • 二、系統調用層
      • 1. 打開關閉文件函數
      • 2. 讀寫文件函數
    • 三、操作系統文件管理
      • 1. 文件管理機制
      • 2. 硬件管理機制
    • 四、理解重定向
      • 1. 文件描述符分配規則
      • 2. 重定向系統調用
      • 3. 重定向命令行調用
    • 五、理解緩沖區
      • 1. 緩沖區介紹
      • 2. 緩沖區刷新策略
      • 3. 有趣現象

一、初步理解系統層面的文件

1. 文件操作的本質

在C語言里文件操作時,fopen打開文件,本質是cpu執行代碼到這一行,進程幫我們創建相應的內核數據結構和相關初始化,打開文件本質是進程打開文件

2. 進程管理文件核心思想

一個進程可以打開多個文件,系統中有許多進程,所以大多數情況下,OS內部,一定存在大量的被打開的文件,同時,操作系統也要進行這些文件的管理

操作系統管理文件與管理進程的方式類似,先描述在組織,管理相應的結構體(類似于進程的pcb)

文件 = 屬性 + 內容

二、系統調用層

1. 打開關閉文件函數

函數原型

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int open(const char *pathname, int flags);int open(const char *pathname, int flags, mode_t mode);int creat(const char *pathname, mode_t mode);#include <unistd.h>int close(int fd);//關閉一個打開的文件,參數fd為打開文件時open的返回值#include <sys/types.h>
#include <sys/stat.h>mode_t umask(mode_t mask); //動態更改當前進程創建文件時的權限掩碼

參數說明

pathname為要打開文件的名稱

falgs參數是用bit位來進行標志的傳遞,即位圖,其含義為代碼打開方式(只讀,只寫,讀寫,追加……),falgs常用選項如下:

O_APPEND 寫的時候追加寫入

O_TRUNC 寫的時候清空文件

O_WRONLY 寫方式打開

O_RDONLY 寫方式打開

O-CREAT 打開時若沒有文件,則在進程當前工作路徑下創建文件

mode為新創建文件時的權限,該權限會由系統的權限掩碼計算后再給設置到新文件

可以通過umask函數在進程內更改該進程創建文件時的權限掩碼

  • umask計算:最終權限 = mode & ~umask
  • 示例:open("file", O_CREAT, 0666) + umask=002 → 實際權限664

返回值

返回值fd,一個整數,稱為文件描述符,對應一個文件內核數據結構(在下文操作系統管理中詳細說明),在后續的文件寫入或者輸出時,傳參的fd都是文件描述符

返回值小于零打開失敗

返回值非零時,打開對應文件會返回對應的值,前3個默認打開,分別是

0:標準輸入 stdin 鍵盤

1:標準輸出 stdout 顯示器

2:標準錯誤 stderror 顯示器

前3個進程啟動時默認打開,一般情況下我們自己打開或者創建文件返回值從3開始依次增加

1和2都對應著顯示器,為什么同時默認打開1和2

1和2對應的文件都是顯示器文件,區別就是,當我們進行標準輸出重定向時,只會將1號文件進行重定向,2號不會被重定向。

默認同時打開的主要原因就是我們輸出信息時,有正確的信息也有錯誤信息,只需做一次輸出重定向就可以將錯誤信息和正確信息分離開,標準輸出重定向只會將1號文件重定向,2號文件不會改變,依舊輸出在顯示器上。

  #include<stdio.h>#include<sys/types.h>#include<sys/stat.h>#include<fcntl.h>#include<unistd.h>                                                                                  int main(){fprintf(stdout,"hello stdout \n");fprintf(stdout,"hello stdout \n");fprintf(stdout,"hello stdout \n");fprintf(stderr,"hello stderr \n");fprintf(stderr,"hello stderr \n");fprintf(stderr,"hello stderr \n");return 0;}

運行結果

在這里插入圖片描述

也可以用兩次重定向(在后文重定向中說明)將正確消息和錯誤消息分開放在不同的文件里,方便我們的調試查看信息。
在這里插入圖片描述

2. 讀寫文件函數

       #include <unistd.h>ssize_t write(int fd, const void *buf, size_t count);
//向文件fd里面寫入buf指向的內容,寫入內容大小為count,返回值大于0表示實際寫入的字節,等于0表示什么都沒寫,返回-1表示失敗,并且設置錯誤碼ssize_t read(int fd, void *buf, size_t count);//向buf里面讀入fd文件的內容,讀入內容大小為count,返回值大于0表示實際讀取的字節,等于0表示讀到了文件結尾,返回-1表示失敗,并且設置錯誤碼#include <sys/types.h>#include <sys/stat.h>#include <unistd.h>int stat(const char *path, struct stat *buf);  //通過路徑獲取文件屬性//buf為輸出型參數,文件屬性存入buf指向的結構體,實現函數功能,成功返回0,失敗返回-1,并且設置錯誤碼int fstat(int fd, struct stat *buf);//通過文件描述符獲取文件屬性int lstat(const char *path, struct stat *buf); //通過路徑獲取文件屬性

內核中的文件屬性(struct stat):

struct stat {dev_t     st_dev;     // 設備IDino_t     st_ino;     // inode號mode_t    st_mode;    // 文件類型和權限nlink_t   st_nlink;   // 硬鏈接數uid_t     st_uid;     // 所有者UIDgid_t     st_gid;     // 組GIDoff_t     st_size;    // 文件大小(字節)// ... 時間戳等字段
};

在這里插入圖片描述

三、操作系統文件管理

1. 文件管理機制

核心思想:先描述,在組織

內核級管理

每打開一個文件時,操作系統要創建相應的內核數據結構(struct file),并且會創建文件內核級的緩存(開辟的一塊空間)。內核數據結構中存在指針指向該緩存,并且會用文件的屬性去初始化內核數據結構,將文件的內容加載到文件緩存里。

一個進程可以打卡多個文件,操作系統需要建立進程和文件的對應關系,所以在進程的pcb(task_struct)中存在一個struct files_struct * files指針(指向文件描述符表),struct files_struct數據結構中包含struct file* fd_array[N],一個文件指針的數組,對應該進程所打開的文件的內核數據結構file,該數組下標就是打開文件時所返回的文件描述符。

操作系統提供的系統調用可以用文件描述符快速找到文件對應的內核數據結構進行操作,在進行讀寫時,都必須在合適的時候讓OS把文件的內容讀寫在緩沖區,在進行刷新

在操作系統內,訪問文件只認文件描述符fd

語言級管理

在C語言中,通過封裝系統調用設計出來一系列的文件操作函數(fprintf,fscnaf,fopen),在配合C語言封裝的文件結構體struct FILE,我們常常定義的文件指針FILE*就是這種結構的指針。

struct FILE中封裝這文件描述符,語言級的緩沖區,打開方式等信息。

在這里插入圖片描述

如圖,int _fileno 為文件描述符,_falgs為文件打開方式, _IO_write_end為該緩沖區的結束……

C語言中所有的文件操作都是對系統調用的封裝。

由于在不同的系統中系統調用是不同的,因此我們寫的含有系統調的代碼不具備跨平臺性,但是我們用C語言庫中的函數,他是具備跨平臺性的,因為我們在不同的平臺下有不同的C語言標準庫,他們底層封裝的系統調用是對應系統的系統調用。

文件打開流程

  1. 創建struct file對象
  2. 分配內核緩沖區(可延遲加載數據)
  3. 查進程的文件描述符表空閑的下標
  4. 存儲file對象地址于文件描述符表中
  5. 返回fd下標

2. 硬件管理機制

在Linux系統中,一切皆文件

硬件設備都會有自己的共同的屬性(名稱,廠商,生產日期等),這些屬性都被封裝在結構體中(struct device),同時,不用的硬件也有自己獨特的操作方法(驅動程序中實現),比如像顯示器上輸出,從鍵盤鼠標內讀取數據等,這些方法都是驅動程序中一個個的函數。

在Linux系統中,打開或者使用某一個外設時,會像管理文件一樣管理硬件,創建一個struct file(文件內核數據結構),存放著對應硬件設備使用的函數的函數指針,還有指向屬性結構體(struct device)的指針和屬于該硬件的緩沖區的指針,向硬件設備中讀或者寫數據時,先在緩沖區操作,然后將緩沖區內容刷新到設備或者內存。

不同的設備的驅動程序中,類似操作的函數參數要設計相同,因為在struct file中函數指針只有一套,但要調用不同設備的方法,參數相同才可以兼容

源碼中部分函數指針:

在這里插入圖片描述

由于struct file在進行文件管理時(硬件也看做文件),即有屬性(struct device,文件屬性),還有方法(操作底層方法指針表,或者操作文件的方法列表),因此這也是一種類的實現,并且用相同的函數名(函數指針)來操作不同類型的硬件設備或者文件,也是用C語言實現的多態技術,這種管理技術在Linux系統中也叫做vfs(virtual file system)

四、理解重定向

1. 文件描述符分配規則

打開文件時,查文件描述符表從0開始分配,還沒有被使用的最小的下標將被分配

重定向的本質是在內核中改變文件描述符表特定下標的內容,與上層無關

2. 重定向系統調用

       #include <unistd.h>int dup(int oldfd);int dup2(int oldfd, int newfd);//將文件描述符表中oldfd下標對應的內容拷貝到newfd對應下標的位置

舉例

  #include<stdio.h>#include<sys/types.h>#include<sys/stat.h>#include<fcntl.h>#include<unistd.h>                                                                                  int main(){const char* filename = "file.txt";//打開文件,不存在文件時新建,以寫入方式打開,每次打開時清空文件int fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0666);//重定向dup2(fd, 1);//默認向stdout中寫入printf("hello Linux\n");//指定向stdout中寫入fprintf(stdout, "hello Linux\n");return 0;}

運行結果:

在這里插入圖片描述

可以看到,本應當向標準輸出stdout中輸出的內容輸出到文件file.txt中了。

因為我們使用了重定向將該進程中文件描述符表里的fd(值為3,因為0,1,2默認被使用,從3開始分配)下標的內容拷貝到1下標位置,因此1號下標本來存放的標準輸出文件對象指針被我們修改為“file.txt”文件的文件對象指針。

在底層的文件描述符表中,1號下標處為我們重定向的file.txt文件對象指針,但是上層stdout(C語言提供的FILE*指針,里面封裝這文件描述符等信息)里面的文件描述符等其他信息都沒有改變,在調用printf,fprintf時都向stdout中打印,stdout中封裝著_fileno = 1,操作系統就會打印到文件描述符表里將下標為1處的文件指針所指的文件對象的緩沖區中,即向我們重定向的文件file.txt中打印。

3. 重定向命令行調用

簡單調用

在指定被重定向的文件時,默認為標準輸出重定向,即文件描述符為1的文件

可執行程序 > filename ,表示標準輸出重定向到filename,不存在該文件時就新建

可執行程序 >> filename, 和> 大致相同,唯一區別就是>的重定向默認清空文件,>>是追加的打印,不會在打開文件時清空,

< filename 表示標準輸入重定向

舉例:

echo hello Linux > log.txt

該命令可以將本來向顯示器打印的 hello Linux 打印到log.txt中

復雜調用

指令或可執行程序 1>filename1 2>filename2 ……,表示將文件描述符為1的文件重定向到filename1, 文件描述符為2的文件重定向到filename2

舉例:

在這里插入圖片描述

指令或可執行程序 1>filename 2>&1,將1號重定向到filename,在&1(1號下標的內容,即filename的地址)放入下標為2,即1,2同時指向filename
在這里插入圖片描述

五、理解緩沖區

1. 緩沖區介紹

緩沖區就是一段內存空間,可以給上層提供高效的IO體驗,間接提高整體的效率,

緩沖區有用戶級緩沖區(語言提供,維護的)和內核級緩沖區(操作系統提供,維護的),緩沖區的優點有解耦(用戶只需將數據交到緩沖區,緩沖區會自己刷新到下一個目標位置,一般不需要我們在進行操作,設計),提高效率。每一個打開的文件都有自己的緩沖區,語言級的緩沖區在struct FILE中(C語言中的文件指針FILE*),內核級的緩沖區在文件對象(struct file)中。

2. 緩沖區刷新策略

1.立即刷新,fflush(stdout),fsync(fd),這類函數調用可以立即刷新緩沖區,可以認為是無緩沖

2.行刷新,顯示器通常采用行刷新

3.全緩沖,緩沖區寫滿,才刷新,普通文件通常采用全緩沖

4.進程退出,系統會自動刷新

3. 有趣現象

有下面2段代碼

代碼1:

#include<stdio.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>int main()
{printf("hello printf\n");fprintf(stdout, "hello fprintf\n");const char* message = "hello write\n";write(1, message, strlen(message));return 0;
}

執行結果
在這里插入圖片描述

代碼2:

#include<stdio.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>int main()
{printf("hello printf\n");fprintf(stdout, "hello fprintf\n");const char* message = "hello write\n";write(1, message, strlen(message));fork();return 0;
}

執行結果

在這里插入圖片描述

現象:

重定向后先輸出的是write,后輸出printf和fprintf,

當創建子進程時,printf和fprintf被輸出兩遍

原因:

我們在命令行進行了重定向執行程序時,會使得原本向stdout輸出的內容輸出到file.txt文件中,這會改變刷新策略,顯示器是按行刷新,普通文件是寫滿緩沖區或者進程退出在刷新,因此當"hello printf\n"和"hello fprintf\n"被寫入用戶緩沖區時,不會直接刷新,而調用write時,該調用是系統調用,無用戶緩沖區,直接寫入內核緩沖區,內核緩沖區直接進行刷新,因此第一行是"hello write",后面程序退出時在將用戶緩沖區的內容刷新,先刷新到內核緩沖區,在刷新到文件,

圖片轉存中…(img-MnhIhwH7-1753197796549)]

現象:

重定向后先輸出的是write,后輸出printf和fprintf,

當創建子進程時,printf和fprintf被輸出兩遍

原因:

我們在命令行進行了重定向執行程序時,會使得原本向stdout輸出的內容輸出到file.txt文件中,這會改變刷新策略,顯示器是按行刷新,普通文件是寫滿緩沖區或者進程退出在刷新,因此當"hello printf\n"和"hello fprintf\n"被寫入用戶緩沖區時,不會直接刷新,而調用write時,該調用是系統調用,無用戶緩沖區,直接寫入內核緩沖區,內核緩沖區直接進行刷新,因此第一行是"hello write",后面程序退出時在將用戶緩沖區的內容刷新,先刷新到內核緩沖區,在刷新到文件,

創建子進程后,用戶緩沖區沒有寫滿還沒有被刷新,內核緩沖區已刷新,父子進程各有自己的用戶緩沖區,父子進程各自刷新一次,所以出現了printf和fprintf打印2次

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

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

相關文章

科技向善,銀發向暖:智慧養老與經濟共筑適老未來

人口老齡化是當今中國社會面臨的重大課題&#xff0c;也是推動社會變革與經濟轉型的重要引擎。隨著數字技術的飛速發展&#xff0c;“智慧養老”正以科技向善的溫度&#xff0c;為老年群體構建更舒適、更安全、更有尊嚴的晚年生活&#xff0c;同時為銀發經濟注入蓬勃活力&#…

numpy庫 降維,矩陣創建與元素的選取,修改

目錄 1.降維函數ravel()和flatten ravel(): flatten(): 2.矩陣存儲與內存結構 3.修改矩陣形狀的方法 4.特殊矩陣創建 全零矩陣: 如np.zeros(5) 創建含5個零的一維數組&#xff0c;輸出中零后的點&#xff08;如 0.&#xff09;表示浮點數類型。 全一矩陣&#xff1a;如n…

SpringCloud seata全局事務

項目https://github.com/apache/incubator-seata docker拉取啟動server $ docker run --name seata-server -p 8091:8091 apache/seata-server:2.1.0 seata注冊到nacos <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-…

OpenLayers 快速入門(八)事件系統

看過的知識不等于學會。唯有用心總結、系統記錄&#xff0c;并通過溫故知新反復實踐&#xff0c;才能真正掌握一二 作為一名摸爬滾打三年的前端開發&#xff0c;開源社區給了我飯碗&#xff0c;我也將所學的知識體系回饋給大家&#xff0c;助你少走彎路&#xff01; OpenLayers…

【Linux | 網絡】應用層(HTTPS)

目錄一、HTTPS的概念二、準備概念2.1 什么是加密和解密2.2 為什么要加密2.3 常見的加密方式2.3.1 對稱加密2.3.1 非對稱加密2.4 數據摘要&&數據指紋三、HTTPS理解過程3.1 只使用對稱加密3.2 只使用非對稱加密3.3 雙方都使用非對稱加密3.4 對稱加密 非對稱加密3.5 中間…

GRE協議

一、實驗拓撲二、實驗配置1、靜態路由實現GRERT1配置&#xff1a;RT1(config)# int fa1/0RT1(config-if)# ip add 192.168.20.1 255.255.255.0RT1(config-if)# no shutdownRT1(config)# int fa0/0RT1(config-if)# ip add 172.1.1.2 255.255.255.0RT1(config-if)# no shutdownRT…

JDialong彈窗

public class DialogDemo extends JFrame {public DialogDemo(){this.setVisible(true);this.setSize(700,500);this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);//JFrame 放東西&#xff0c;容器Container contentPane this.getContentPane();//絕對布局conte…

tlias智能學習輔助系統--違紀處理(實戰)

目錄 1.StudentController.java 2.interface StudentService 3.StudentServiceImpl.java 4.interface StudentMapper 1.StudentController.java // 違紀處理PutMapping("/violation/{id}/{score}")Operation(summary "違紀處理")public Result violat…

傳統RNN模型筆記:輸入數據長度變化的結構解析

一、案例背景 本案例通過PyTorch的nn.RNN構建單隱藏層RNN模型&#xff0c;重點展示RNN對變長序列數據的處理能力&#xff08;序列長度從1變為20&#xff09;&#xff0c;幫助理解RNN的輸入輸出邏輯。 二、核心代碼與結構拆解 def dm_rnn_for_sequencelen():# 1. 定義RNN模型rnn…

OpenLayers 快速入門(四)View 對象

看過的知識不等于學會。唯有用心總結、系統記錄&#xff0c;并通過溫故知新反復實踐&#xff0c;才能真正掌握一二 作為一名摸爬滾打三年的前端開發&#xff0c;開源社區給了我飯碗&#xff0c;我也將所學的知識體系回饋給大家&#xff0c;助你少走彎路&#xff01; OpenLayers…

測試左移方法論

測試左移&#xff08;Shift-Left Testing&#xff09;?是一種軟件測試方法論&#xff0c;核心思想是將測試活動從傳統的開發后期&#xff08;如系統測試、驗收測試階段&#xff09;提前到軟件生命周期的更早期階段&#xff08;如需求分析、設計、編碼階段&#xff09;&#xf…

OpenCV(01)基本圖像操作、繪制,讀取視頻

圖像基礎 import cv2 as cv#讀取圖像 cv.imread(path,讀取方式)默認讀為彩色圖像 #cv.imread(path) cat cv.imread(E:\hqyj\code\opencv\images\\face.png)#顯示圖像 cv.imshow(window,img) cv.imshow(myimg,cat)print(cat) print(cat.shape) #(h,w,c) 元組(1,1) print(cat…

biji 1

1.應用層&#xff1a;為應用程序提供網絡服務。2.表示層&#xff1a;定義數據的格式&#xff0c;對數據進行壓縮、解壓縮、加密、解密、編碼、解碼。3.會話層&#xff1a;對通信雙方間的會話進行建立、維護、拆除-----session id---區分同一應用程序的不同進程4.傳輸層&#x…

mongodb的備份和還原(精簡)

1 官網下載對應版本msi2 運行msi mongodb-database-tools-windows-x86_64-100.12.2.msi3 將安裝地址加到環境變量 C:\Program Files\MongoDB\Tools\100\bin4 查看version mongodump --version mongorestore --version5 運行 備份命令 mongodump --host 127.0.0.1 --db dbname--…

Mac安裝Typescript報錯

目錄 Mac上安裝Typescript報錯: 原因分析 1. 默認 npm 全局安裝目錄的權限問題 2. Node.js 的安裝方式 如何解決?(無需每次用 `sudo`) 方法 1:修改 npm 全局目錄的權限(推薦) 方法 2:配置 npm 使用用戶級目錄 方法 3:使用 `nvm` 管理 Node.js(最推薦) 為什么建議避免…

spring-cloud概述

單體架構 把業務的所有功能實現都打包在一個war包或者jar包&#xff0c;這種方式就成為單體架構。 比如Spring課程中的博客系統,前端后端數據庫實現&#xff0c;都在一個項目中&#xff0c;這種架構就稱為單體架構. 舉個例子&#xff1a; 比如在電商系統中&#xff0c;我們…

android ROOM kotlin官方文檔完全學習

android ROOM kotlin官方文檔完全學習2.6 使用 Room 將數據保存到本地數據庫 | Android Developers (google.cn) 一、簡介 1.1 引入 dependencies {def room_version "2.6.1"implementation "androidx.room:room-runtime:$room_version"//如下三選一a…

DOM編程全解析:操作、事件與存儲實戰指南

引言&#xff1a;DOM——JavaScript與網頁交互的橋梁 DOM&#xff08;文檔對象模型&#xff09; 是JavaScript操作HTML/XML文檔的接口&#xff0c;它將網頁文檔抽象為一個樹形結構&#xff0c;允許開發者通過API動態修改文檔的內容、結構和樣式。無論是實現動態交互&#xff0…

Ansible命令

Ansible命令 ansible 常用命令 /usr/bin/ansible   #Ansibe AD-Hoc 臨時命令執行工具&#xff0c;常用于臨時命令的執行 /usr/bin/ansible-doc    #Ansible 模塊功能查看工具 /usr/bin/ansible-galaxy   #下載/上傳優秀代碼或Roles模塊 的官網平臺&#xff0c;基于網…

SY6974芯片添加enable充電控制節點

1. 需求描述項目背景&#xff1a;基于 Qualcomm MDM9x07 平臺的 4G MIFI 產品&#xff0c;使用 Silergy 公司的 SY6974 充電 IC需求內容&#xff1a; 在環境 /sys/class/power_supply/sy6794/enable 下增加一個 sysfs 節點&#xff0c;用于控制是否允許充電&#xff1a;cat /sy…