搜尋
首頁系統教程Linux24小時學通Linux核心之有關Linux檔案系統實作的問題

24小時學通Linux核心之有關Linux檔案系統實作的問題

Feb 05, 2024 pm 04:00 PM
linuxlinux教程linux系統linux指令shell腳本嵌入式linux良許linux入門linux學習

Linux的使用和使用者空間程式的程式設計與檔案系統密切相關。對於檔案系統的概念,大家可能已經比較熟悉了,所以我不會過度講解。畢竟,只要能了解這些概念就可以了,對於想深入了解的人,可以隨時透過百度等搜尋引擎獲取更多資訊。現在我將重點放在Linux的虛擬檔案系統。

虛擬檔案系統是Linux的一個重要特性之一,它支援多種不同的檔案系統。文件系統的架構如下圖所示:[圖片見原文] 24小時學通Linux核心之有關Linux檔案系統實作的問題

上圖中VFS(虛擬檔案系統)依賴資料結構來保存其對一個檔案系統的一般表示,其中資料結構羅列如下:

  • 超級區塊結構:存放已經安裝的檔案系統的相關資訊;

  • 索引結點結構:存放有關文件的 資訊;

  • 檔案結構:存放已行程開啟的檔案的相關資訊;

  • 目錄項目結構:存放有關路徑名和路徑名所指向的檔案的資訊。

    Linux核心使用全域變數來保存先前提到的指向結構體的指針,所有的結構都用雙向鍊錶保存,核心保存指向鍊錶頭的指針,並且把它當作鍊錶的存取點,這些結構都用list_head類型的域,用它來指向鍊錶中的前一個元素,下表是內核保存的全域變數以及這些變數指向的鍊錶類型(與VFS相關的全域變數)

全域變數 結構類型
super_blocks #super_block
file_systems #file_systems_type
dentry_unused dentry
vfsmntlist vfsmount
inode_in_use inode
inode_unused inode
#

super_block、file_system_type、dentry、vfsmoubt結構都保存在它們自己的鍊錶中,索引結點能夠在全域的inode_in_use上或inode_unused上找到自己,或者它們對應的超快的局部鍊錶上都可以找到自己。

除了主要的VFS結構之外,還有幾個其他的結構與VFS相互作用,fs_struct和files_struct,namespace,fd_set,下圖講訴了進程描述符是如何與檔案相關的結構相關聯的。

24小時學通Linux核心之有關Linux檔案系統實作的問題

先來介紹fs_struct結構,fs_struct結構可以被多個行程描述子引用,下述程式碼在include/Linux/fs_struct.h中可以查到哦,程式碼解釋不好的請大神指教

struct fs_struct{
    atomic_t count;  //保存引用特定fs_struct的进程描述符数目
    rwlock_t lock;
    int umask;  //保存一个掩码,表示将要在打开文件上设置的许可权
    struct dentry * root, *pwd ,*altroot;  //都是指针,,,,
    struct vfsmount * rootmnt, *pwdmnt,  *altrootmnt;  //指针,
};

files_struct包含開啟檔案和其描述符的相關訊息,它使用這些集合來對它的描述符進行分組。下面程式碼可以在include/linux/file.h上查看到

struct files_struct{
    atomic_t count;  //与fs_struct类似
    spinlock_t file_lock;
    int max_fds;  //表示进程能够打开的文件的最大数
    int max_fdset;  //表示描述符的最大数
    int next_fd;  //保存下一个将要分配的文件描述符的值
    struct file ** fd;  //fd数组指向打开的文件对象的数组
    fd_set *close_on_exec; //是指向文件描述符集的一个指针,这些文件描述符在exec()时候就被标志位将要关闭,如果在exec()时候被标志位“打开”的文件描述符数超过close_on_exec_init域的大小,则改变close_on_exec域的值;
    fd_set *open_fds; //是一个指针,指向被标记为“打开”的文件描述符集合,
    fd_set close_on_exec_init;  //保存一个位域,表示打开文件对应的文件描述符
    fd_set open_fds_init;    //这些都是fd_set类型的域,其实都不懂,,,
    struct file *fd_array[NR_OPEN_DEFAULT];//fd_array数组指针指向前32个打开的文件描述法
};

透過INIT_FILES巨集初始化fs_struct結構:

#define INIT_FILES \
{
    .count = ATOMIC_INIT(1),
    .file_lock = SPIN_LOCK_UNLOCKED,
    .max_fds = NR_OPEN_DEFAULT,
    .max_fdset = __FD_SETSIZE,
    .next_fd = 0,
    .fd = &init_files.fd_array[0];
    .close_on_exec = &init_files.close_on_exec_init,
    .open_fds = &init_files.open_fds_init,
    .close_on_exec_init = {{0, }},
    .open_fda_init = {{0, }},
    .fd_array = {NULL, }
}

NR_OPEN_DEFAULT的全域定義被設定為BITS_PER_LONG,BITS_PER_LONG在32位元系統中是32,在64位元系統中是64.

下面來介紹一下頁緩衝,我們現在看看它是如何運作和實現的。在Linux中,記憶體被分成區,每個擁有活躍頁的鍊錶和不活躍的鍊錶,當頁不活躍的時候,就會被寫回磁碟,下圖說明了上述關係:

24小時學通Linux核心之有關Linux檔案系統實作的問題

image-20240202221039708

頁緩衝的核心是address_space對象,其程式碼在include/linux/fs.h中可以查看(這段程式碼不是很懂,求大神指教):

struct address_space{    
    struct inode *host;
    struct radix_tree_root page_tree;
    spinlock_t tree_lock;
    unsigned long nrpages;
    pgoff_t writeback;
    struct address_space_operations *a_ops;
    struct prio_tree_root i_map;
    unsigned inr i_map_lock;
    struct list_head i_mmap_nonlinear;
    spinlock_t i_mmap_lock;
    atomic_t truncate_count;
    unsigned long flags;
    struct backing_dev_info *backing_dev_info;
    spinlock_t private_lock;
    struct list_head private_list;
    struct address_space *assoc_mapping;
};

Linux核心也把區塊裝置上的每個磁區表示buffer_head結構,buffer_head結構應用的物理區是裝置b_dev的邏輯區塊b_blocknr,引用的實體記憶體是起始在區塊大小為b_size個位元組的b_data記憶體資料區塊,這個記憶體區塊在物理頁b_page中,其結構如下圖:

24小時學通Linux核心之有關Linux檔案系統實作的問題

最後來說說VFS系統呼叫和檔案系統層,並且追蹤它們的執行直到核心級別,我們得先了解四個函數:open()、close()、read()、write() 。

open()函數:

open 函數用於開啟和建立檔案。以下是 open 函數的簡單描述

#include 
int open(const char *pathname, int oflag, ... );

傳回值:成功則傳回檔案描述符,否則傳回 -1

對於 open 函數來說,第三個參數(…)僅在建立新檔案時才使用,用於指定檔案的存取權位元(access permission bits)。 pathname 是待開啟/建立檔案的路徑名稱(如 C:/cpp/a.cpp);oflag 用來指定檔案的開啟/建立模式,這個參數可由下列常數(定義於 fcntl.h)透過邏輯或構成。

  • O_RDONLY 唯讀模式

  • O_WRONLY 只寫模式

  • O_RDWR 讀寫模式

  • 開啟/建立檔案時,至少得使用上述三個常數中的一個。以下常數是選用的:

  • O_APPEND 每次寫入作業都會寫入檔案的結尾

  • O_CREAT 如果指定檔案不存在,則建立這個檔案

  • O_EXCL 如果要建立的檔案已存在,則傳回 -1,並且修改 errno 的值

  • O_TRUNC 如果檔案存在,並且以唯寫/讀寫方式打開,則清空檔案全部內容

  • O_NOCTTY 如果路徑名稱指向終端設備,不要把這個裝置當作控制終端。

  • O_NONBLOCK 如果路徑名稱指向 FIFO/區塊檔案/字元文件,則把檔案的開啟和後繼 I/O設定為非阻塞模式(nonblocking mode)

  • 以下三個常數同樣是選用的,它們用來同步輸入輸出

  • O_DSYNC 等待物理 I/O 結束後再 write。在不影響讀取新寫入的資料的前提下,不等待檔案屬性更新。

  • O_RSYNC read 等待所有寫入相同區域的寫入操作完成後再進行

  • O_SYNC 等待物理 I/O 結束後再 write,包括更新檔案屬性的 I/O

    open 傳回的檔案描述符一定是最小的未被使用的描述符。

    如果NAME_MAX(檔案名稱最大長度,不包含'\0')是14,而我們想在目前目錄下建立檔案名稱長度超過14 個位元組的文件,早期的System V 系統(如SVR2)會截斷超出部分,只保留前14 個位元組;而由BSD 衍生的(BSD-derived)系統會傳回錯誤訊息,並且把errno 置為ENAMETOOLONG。

    POSIX.1 引入常量 _POSIX_NO_TRUNC 用于决定是否截断长文件名/长路径名。如果_POSIX_NO_TRUNC 设定为禁止截断,并且路径名长度超过 PATH_MAX(包括 ‘\0’),或者组成路径名的任意文件名长度超过 NAME_MAX,则返回错误信息,并且把 errno 置为 ENAMETOOLONG。

close()函数

进程使用完文件后,发出close()系统调用:

sysopsis

#include 
int close(int fd);

参数:fd文件描述符

函数返回值:0成功,-1出错

参数fd是要关闭的文件描述符。需要说明的是:当一个进程终止时,内核对该进程所有尚未关闭的文件描述符调用close关闭,所以即使用户程序不调用close,在终止时内核也会自动关闭它打开的所有文件。但是对于一个长年累月运行的程序(比如网络服务器),打开的文件描述符一定要记得关闭,否则随着打开的文件越来越多,会占用大量文件描述符和系统资源。

read()函数

当用户级别程序调用read()函数时,Linux把它转换成系统调sys_read():

功能描述:从文件读取数据。
所需头文件: #include

函数原型:ssize_t read(int fd, void *buf, size_t count);

参数

  • fd: 将要读取数据的文件描述词。

  • buf:指缓冲区,即读取的数据会被放到这个缓冲区中去。

  • count:表示调用一次read操作,应该读多少数量的字符。

  • 返回值:返回所读取的字节数;0(读到EOF);-1(出错)。

  • 以下几种情况会导致读取到的字节数小于 count :

  • 读取普通文件时,读到文件末尾还不够 count 字节。例:如果文件只有 30 字节,而我们想读取 100,字节,那么实际读到的只有 30 字节, 函数返回 30 。此时再使用 read 函数作用于这个文件会导致 read 返回 0

  • 从终端设备(terminal device)读取时,一般情况下每次只能读取一行。

  • 从网络读取时,网络缓存可能导致读取的字节数小于 count字节。

  • 读取 pipe 或者 FIFO 时,pipe 或 FIFO 里的字节数可能小于 count 。

  • 从面向记录(record-oriented)的设备读取时,某些面向记录的设备(如磁带)每次最多只能返回一个记录。

  • 在读取了部分数据时被信号中断,读操作始于 cfo 。在成功返回之前,cfo 增加,增量为实际读取到的字节数。

    例程如下(程序是网上找的例子,贴下来以以供大家理解一下)::

#include 
#include 
#include 
#include 
#include 
#include 
int main(void)
{
    void* buf ;
    int handle;
    int bytes ;
    buf=malloc(10);
    /*
    LooksforafileinthecurrentdirectorynamedTEST.$$$andattempts
    toread10bytesfromit.Tousethisexampleyoushouldcreatethe
    fileTEST.$$$
    */
    handle=open("TEST.$$$",O_RDONLY|O_BINARY,S_IWRITE|S_IREAD);
    if(handle==-1)
    {
        printf("ErrorOpeningFile\n");
        exit(1);
    }
    bytes=read(handle,buf,10);
    if(bytes==-1)
    {
        printf("ReadFailed.\n");
        exit(1);
    }
    else 
    {
        printf("Read:%dbytesread.\n",bytes);
    }
    return0 ;
}

write()函数

功能描述:向文件写入数据。
所需头文件: #include

函数原型:ssize_t write(int fd, void *buf, size_t count);

返回值:写入文件的字节数(成功);-1(出错)

功能:write 函数向 filedes 中写入 count 字节数据,数据来源为 buf 。返回值一般总是等于 count,否则就是出错了。常见的出错原因是磁盘空间满了或者超过了文件大小限制。对于普通文件,写操作始于 cfo 。如果打开文件时使用了 O_APPEND,则每次写操作都将数据写入文件末尾。成功写入后,cfo 增加,增量为实际写入的字节数。

例程如下(程序是网上找的例子,贴下来以以供大家理解一下):

#include 
#include 
#include 
#include 
#include 
#include 
int main(void)
{
int *handle; char string[40];
int length, res;/* Create a file named "TEST.$$$" in the current directory and write a string to it. If "TEST.$$$" already exists, it will be overwritten. */
if ((handle = open("TEST.$$$", O_WRONLY | O_CREAT | O_TRUNC, S_IREAD | S_IWRITE)) == -1)
{
printf("Error opening file.\n");
exit(1);
}
strcpy(string, "Hello, world!\n");
length = strlen(string);
if ((res = write(handle, string, length)) != length)
{
printf("Error writing to the file.\n");
exit(1);
}
printf("Wrote %d bytes to the file.\n", res);
close(handle); return 0; }

小结

今天看的代码不多,差不多都是网上找的代码,有些解释也是查阅资料写上去的,有些还是不懂,希望各路大神指教,这里我总结了有关Linux文件系统实现的问题,但是具体的细节方面并没有提及到,大家看了之后应该只能有一个大致的最Linux文件系统的了解,有读者问我看的是哪些书,这里我说明一下,看了Linux内核编程,还有深入理解Linux内核以及网上各种资料或者其他大牛写的好的博客。这里我是总结了一下,并且把自己不懂的还有觉得重要的说了一下,希望各位大神给些建议,thanks~

以上是24小時學通Linux核心之有關Linux檔案系統實作的問題的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文轉載於:良许Linux教程网。如有侵權,請聯絡admin@php.cn刪除
Linux故障排除:5個常見問題以及如何解決這些問題Linux故障排除:5個常見問題以及如何解決這些問題Apr 29, 2025 am 09:42 AM

Linux系统以其强大和可靠性著称,但即使是经验丰富的用户也会遇到意想不到的问题。无论是意外删除的文件、忘记的root密码,还是系统运行缓慢,高效的故障排除技巧是成为Linux专家的关键。 本指南将介绍一些常见的Linux问题解决场景以及逐步解决方案,这些问题在系统管理员、开发人员和日常Linux用户中普遍存在。 场景一:意外删除重要文件 您意外地使用rm命令删除了一个重要文件,现在需要恢复它。与Windows和macOS不同,Linux没有内置的“回收站”来存储从终端删除的文件。 恢复选项取决

如何在Linux上永久更改Docker文件夾權限如何在Linux上永久更改Docker文件夾權限Apr 29, 2025 am 09:35 AM

Docker 是一款強大的工具,允許您在稱為 容器 的隔離環境中運行應用程序。但是,有時您可能需要更改 Docker 文件夾的權限,以確保您的應用程序可以訪問必要的文 件和目錄。 本文將指導您完成在 Linux 系統上永久更改 Docker 文件夾權限的過程。 了解 Docker 文件夾權限 默認情況下,Docker 將其數據(包括鏡像、容器和卷)存儲在 Linux 系統上的特定目錄中。最常見的目錄是 /var/lib/docker。 這些文件夾的權限決定了誰可以讀取、寫入或執行其中的文件。如果

像專業人士一樣管理Docker:在Linux上安裝Portainer CE像專業人士一樣管理Docker:在Linux上安裝Portainer CEApr 29, 2025 am 09:24 AM

用Linux上的Portainer CE簡化Docker Management:逐步指南 通過命令行管理Docker容器可能令人生畏,尤其是對於新移民而言。 Portainer CE(社區版)提供免費,輕巧且直觀的Solutio

如何在Linux上使用Whisper AI進行現場音頻轉錄如何在Linux上使用Whisper AI進行現場音頻轉錄Apr 29, 2025 am 09:18 AM

本指南詳細介紹瞭如何在Linux系統上安裝和使用Whisper AI進行實時語音到文本轉錄。 Whisper AI是一種Openai創作,可提供多種語言的高準確性轉錄。雖然主要是為批處理設計的

超級文件:Linux的理想基於終端的文件經理超級文件:Linux的理想基於終端的文件經理Apr 29, 2025 am 09:16 AM

對於Linux終端愛好者,強大的文件經理至關重要。 儘管存在許多人,但Superfile卻是一種現代,輕巧且視覺上吸引人的選擇。本文探討了超級文件,其起源,以及為什麼它是您F的最大競爭者

Zellij:Linux用戶的現代終端多路復用器Zellij:Linux用戶的現代終端多路復用器Apr 29, 2025 am 09:08 AM

Zellij:用於增強Linux工作流的現代終端多路復用器 Linux終端多路復用器是針對開發人員和系統管理員的必不可少的工具,可以簡化命令行交互。 Zellij,一個相對較新的開源多路復用器

Linux和Windows之間的引導過程有何不同?Linux和Windows之間的引導過程有何不同?Apr 29, 2025 am 12:12 AM

Linux的啟動過程包括:1.啟動BIOS/UEFI,2.加載GRUB,3.加載內核和initrd,4.執行init進程,5.啟動系統服務,6.啟動登錄管理器;Windows的啟動過程包括:1.啟動BIOS/UEFI,2.加載WindowsBootManager,3.加載winload.exe,4.加載ntoskrnl.exe和HAL,5.啟動系統服務,6.啟動登錄屏幕;Linux提供更多的自定義選項,而Windows則更注重用戶體驗和穩定性。

如何在Linux中自動重新啟動失敗的服務如何在Linux中自動重新啟動失敗的服務Apr 28, 2025 am 09:39 AM

本指南詳細介紹瞭如何使用SystemD配置自動服務在Linux中重新啟動,從而增強了系統的可靠性並最大程度地減少停機時間。 系統管理員通常依靠此功能來確保關鍵服務,例如Web服務器(APA

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

Dreamweaver Mac版

Dreamweaver Mac版

視覺化網頁開發工具

mPDF

mPDF

mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),

SublimeText3 Linux新版

SublimeText3 Linux新版

SublimeText3 Linux最新版

Atom編輯器mac版下載

Atom編輯器mac版下載

最受歡迎的的開源編輯器

PhpStorm Mac 版本

PhpStorm Mac 版本

最新(2018.2.1 )專業的PHP整合開發工具