Linux 2.4.30 核心檔案系統學習(多圖) 1 : 關鍵資料結構
原文地址:http://blog.csdn.net/rstevens/article/details/1824785
1. 概述
根據以前學習核心原始碼的經驗,在學習檔案系統實現之前,我大概定了個目標:
1、 建立一個清晰的全域性概念。為將來需要研究程式碼細節打下堅實基礎。
2、 只研究虛擬檔案系統 VFS 的實現,不研究具體檔案系統。
為什麼選擇 Linux 2.4.30?因為可以參考《Linux 原始碼情景分析》一書,減少學習難度。
1.1. 基本概
1、 一塊磁碟(塊裝置),首先要按照某種檔案系統(如 NTFS)格式進行格式化,然後才能在其上進行建立目錄、儲存檔案等操作。
在 Linux 中,有“安裝”檔案系統和“解除安裝”檔案系統的概念。
一塊經過格式化的“塊裝置”(不管是剛剛格式化完的,沒有建立任何名錄和檔案;還是已經建立了目錄和檔案),只有先被“安裝”,才能融入 Linux 的檔案系統中,使用者才可以在它上面進行正常的檔案操作。
2、 Linux 把目錄或普通檔案,統一看成“目錄節點”。通常一個“目錄節點”具有兩個重要屬性:名稱以及磁碟上實際對應的資料。本文中,“目錄節點”有時簡稱為“節點”
“符號連結”是一種特殊的目錄節點,它只有一個名稱,沒有實際資料。這個名稱指向一個實際的目錄節點。
3、 “介面結構”:在 核心程式碼中,經常可以看到一種結構,其成員全部是函式指標,例如:
struct file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char *, size_t, loff_t *);
int (*readdir) (struct file *, void *, filldir_t);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, struct dentry *, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);
ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
};
這種結構的作用類似與 C++ 中的“介面類”,它是用 C 語言進行軟體抽象設計時最重要的工具。通過它,將一組通用的操作抽象出來,核心的程式碼只針對這種“介面結構”進行操作,而這些函式的具體實現由不同的“子類”去完成。
以這個 file_operations“介面”為例,它是“目錄節點”提供的操作介面。不同的檔案系統需要提供這些函式的具體實現。
本文中,“介面結構”有時簡稱“介面”。
1.2. 虛擬檔案系統
Linux 通過虛擬檔案系統 (VFS) 來支援不同的具體的檔案系統,那麼 VFS 到底是什麼?
從程式設計師的角度看,我認為 VFS 就是一套程式碼框架(framework),它將使用者與具體的檔案系統隔離開來,使得使用者能夠通過這套框架,以統一的介面在不同的具體的檔案系統上進行操作。
這套框架包括:
1、 為使用者提供統一的檔案和目錄的操作介面,如 open, read, write
2、 抽象出檔案系統共有的一些結構,包括“目錄節點”inode、“超級塊”super_block 等。
3、 面向具體的檔案系統,定義一系列統一的操作“介面”, 如 file_operations, inode_operations, dentry_operation,具體的檔案系統必須提供它們的實現。
4、 提供一套機制,讓具體的檔案系統融入 VFS 框架中,包括檔案系統的“註冊”和“安裝”
5、 實現這套框架邏輯的核心程式碼
我對檔案系統的學習,實際上就是學習虛擬檔案系統這套框架是如何實現的。
2. 核心資料結構
資料結構是程式碼的靈魂,要分析一個複雜的系統,關鍵是掌握那些核心的資料結構,這包括:
1、 弄清資料結構的核心功能。一個資料結構通常具有比較複雜的成員,此外,還有一些成員用於建立資料結構之間的關係。如果要一個個去理解,就會陷入細節。
2、 弄清資料結構之間的靜態關係
3、 弄清資料結構之間是如何建立起動態的關係的
本文重點分析檔案系統中的關鍵資料結構以及它們之間的關係。
2.1. inode 和 file_operations
1、 inode 用以描述“目錄節點” ,它描述了一個目錄節點物理上的屬性,例如大小,建立時間,修改時間、uid、gid 等
2、 file_operations 是“目錄節點”提供的操作“介面”。它包括 open, read, wirte, ioctl, llseek, mmap 等操作。
3、 一個 inode 通過成員 i_fop 對應一個 file_operations
4、 開啟檔案的過程就是尋找 “目錄節點”對應的 inode 的過程
5、 檔案被開啟後,inode 和 file_operation 都已經在記憶體中建立,file_operations 的指標也已經指向了具體檔案系統提供的函式,此後都檔案的操作,都由這些函式來完成。
例如開啟了一個普通檔案 /root/file,其所在檔案系統格式是 ext2,那麼,記憶體中結構如下:
2.2. 目錄節點入口dentry
本來,inode 中應該包括“目錄節點”的名稱,但由於符號連結的存在,導致一個物理檔案可能有多個檔名,因此把和“目錄節點”名稱相關的部分從 inode 中分開,放在一個專門的dentry 結構中。這樣:
1、 一個dentry 通過成員 d_inode 對應到一個 inode上,尋找 inode 的過程變成了尋找dentry 的過程。因此,dentry 變得更加關鍵,inode 常常被 dentry 所遮掩。可以說,dentry 是檔案系統中最核心的資料結構,它的身影無處不在。
2、 由於符號連結的存在,導致多個 dentry 可能對應到同一個 inode 上
例如,有一個符號連結 /tmp/abc 指向一個普通檔案 /root/file,那麼 dentry 與 inode 之間的關係大致如下:
]--><!--[endif]-->
2.3. super_block 和 super_operations
一個存放在磁碟上的檔案系統如 EXT2 等,在它的格式中通常包括一個“超級塊”或者“控制塊”的部分,用於從整體上描述檔案系統,例如檔案系統的大小、是否可讀可寫等等。
虛擬檔案系統中也通過“超級塊”這種概念來描述檔案系統整體的資訊,對應的結構是 struct super_block。
super_block 除了要記錄檔案大小、訪問許可權等資訊外,更重要的是提供一個操作“介面”super_operations。
struct inode *(*alloc_inode)(struct super_block *sb);
void (*destroy_inode)(struct inode *);
void (*read_inode) (struct inode *);
void (*read_inode2) (struct inode *, void *) ;
void (*dirty_inode) (struct inode *);
void (*write_inode) (struct inode *, int);
void (*put_inode) (struct inode *);
void (*delete_inode) (struct inode *);
void (*put_super) (struct super_block *);
void (*write_super) (struct super_block *);
int (*sync_fs) (struct super_block *);
void (*write_super_lockfs) (struct super_block *);
void (*unlockfs) (struct super_block *);
int (*statfs) (struct super_block *, struct statfs *);
int (*remount_fs) (struct super_block *, int *, char *);
void (*clear_inode) (struct inode *);
void (*umount_begin) (struct super_block *);
struct dentry * (*fh_to_dentry)(struct super_block *sb, __u32 *fh, int len, int fhtype, int parent);
int (*dentry_to_fh)(struct dentry *, __u32 *fh, int *lenp, int need_parent);
int (*show_options)(struct seq_file *, struct vfsmount *);
};
我們通過分析“獲取一個 inode ”的過程來只理解這個“介面”中兩個成員 alloc_inode 和 read_inode 的作用。
在檔案系統的操作中,經常需要獲得一個“目錄節點”對應的 inode,這個 inode 有可能已經存在於記憶體中了,也可能還沒有,需要建立一個新的 inode,並從磁碟上讀取相應的資訊來填充。
對應的程式碼是 iget() (inlcude/linux/fs.h)過程如下:
1、 <!--[endif]-->通過 iget4_locked() 獲取 inode。如果 inode 在記憶體中已經存在,則直接返回;否則建立一個新的 inode
2、 <!--[endif]-->如果是新建立的 inode,通過 super_block->s_op->read_inode() 來填充它。也就是說,如何填充一個新建立的 inode, 是由具體檔案系統提供的函式實現的。
iget4_locked() 首先在全域性的 inode hash table 中尋找,如果找不到,則呼叫 get_new_inode() ,進而呼叫alloc_inode() 來建立一個新的 inode
在 alloc_inode() 中可以看到,如果具體檔案系統提供了建立 inode 的方法,則由具體檔案系統來負責建立,否則採用系統預設的的建立方法。
{
static struct address_space_operations empty_aops;
static struct inode_operations empty_iops;
static struct file_operations empty_fops;
struct inode *inode;
if (sb->s_op->alloc_inode)
inode = sb->s_op->alloc_inode(sb);
else {
inode = (struct inode *) kmem_cache_alloc(inode_cachep, SLAB_KERNEL);
if (inode)
memset(&inode->u, 0, sizeof(inode->u));
}
if (inode) {
struct address_space * const mapping = &inode->i_data;
inode->i_sb = sb;
inode->i_dev = sb->s_dev;
inode->i_blkbits = sb->s_blocksize_bits;
inode->i_flags = 0;
atomic_set(&inode->i_count, 1);
inode->i_sock = 0;
inode->i_op = &empty_iops;
inode->i_fop = &empty_fops;
inode->i_nlink = 1;
atomic_set(&inode->i_writecount, 0);
inode->i_size = 0;
inode->i_blocks = 0;
inode->i_bytes = 0;
inode->i_generation = 0;
memset(&inode->i_dquot, 0, sizeof(inode->i_dquot));
inode->i_pipe = NULL;
inode->i_bdev = NULL;
inode->i_cdev = NULL;
mapping->a_ops = &empty_aops;
mapping->host = inode;
mapping->gfp_mask = GFP_HIGHUSER;
inode->i_mapping = mapping;
}
return inode;
}
super_block 是在安裝檔案系統的時候建立的,後面會看到它和其它結構之間的關係。
3. 安裝檔案系統
1、 一個經過格式化的塊裝置,只有安裝後,才能融入 Linux 的 VFS 之中。
2、 安裝一個檔案系統,必須指定一個目錄作為安裝點。
3、 一個裝置可以同時被安裝到多個目錄上。
4、 如果某個目錄下原來有一些檔案和子目錄,一旦將一個裝置安裝到目錄下後,則原有的檔案和子目錄消失。因為這個目錄已經變成了一個安裝點。
5、 一個目錄節點下可以同時安裝多個裝置。
3.1. “根安裝點”、“根裝置”和“根檔案系統”
安裝一個檔案系統,除了需要“被安裝裝置”外,還要指定一個“安裝點”。“安裝點”是已經存在的一個目錄節點。例如把 /dev/sda1 安裝到 /mnt/win 下,那麼 /mnt/win 就是“安裝點”。
可是檔案系統要先安裝後使用。因此,要使用 /mnt/win 這個“安裝點”,必然要求它所在檔案系統已也經被安裝。
也就是說,安裝一個檔案系統,需要另外一個檔案系統已經被安裝。
這是一個雞生蛋,蛋生雞的問題:最頂層的檔案系統是如何被安裝的?
答案是,最頂層檔案系統的時候是被安裝在“根安裝點”上的,而根安裝點不屬於任何檔案系統,它對應的dentry 、inode 是由核心在初始化階段憑空構造出來的。
最頂層的檔案系統叫做“根檔案系統”。Linux 在啟動的時候,要求使用者必須指定一個“根裝置”,核心在初始化階段,將“根裝置”安裝到“根安裝點”上,從而有了根檔案系統。這樣,檔案系統才算準備就緒。此後,使用者就可以通過 mount 命令來安裝新的裝置。
3.2. 安裝連線件 vfsmount
“安裝”一個檔案系統涉及“被安裝裝置”和“安裝點”兩個部分,安裝的過程就是把“安裝點”和“被安裝裝置”關聯起來,這是通過一個“安裝連線件”結構 vfsmount 來完成的。
vfsmount 將“安裝點”dentry 和“被安裝裝置”的根目錄節點 dentry 關聯起來。
每安裝一次檔案系統,會導致:
1、 建立一個 vfsmount
2、 為“被安裝裝置”建立一個 super_block,並由具體的檔案系統來設定這個 super_block。(我們在“註冊檔案系統”一節將再來分析這一步)
3、 為被安裝裝置的根目錄節點建立 dentry
4、 為被安裝裝置的根目錄節點建立 inode, 並由 super_operations->read_inode() 來設定此 inode
5、 將 super_block 與“被安裝裝置“根目錄節點 dentry 關聯起來
6、 將 vfsmount 與“被安裝裝置”的根目錄節點 dentry 關聯起來
在核心將根裝置安裝到“根安裝點”上後,記憶體中有如下結構關係:
現在假設我們在 /mnt/win 下安裝了 /dev/sda1, /dev/sda1 下有 dir1,然後又在 dir1 下安裝了 /dev/sda2,那麼記憶體中就有了如下的結構關係
4. 註冊檔案系統
前面說了,在安裝一個檔案系統的時候,需要為“被安裝裝置”建立一個 super_block,並設定它。
如果從原始碼追尋這個建立和設定 super_block 的過程,就引出了“註冊檔案系統”的概念。
實際上,在安裝一個檔案系統之前,還需要有一個註冊檔案系統的步驟,否則核心就因為不認識該檔案系統而無法完成安裝。
通過register_filesystem() ,將一個“檔案系統型別”結構 file_system_type註冊到核心中一個全域性的連結串列file_systems 上。
const char *name;
int fs_flags;
struct super_block *(*read_super) (struct super_block *, void *, int);
struct module *owner;
struct file_system_type * next;
struct list_head fs_supers;
};
int register_filesystem(struct file_system_type * fs)
{
int res = 0;
struct file_system_type ** p;
if (!fs)
return -EINVAL;
if (fs->next)
return -EBUSY;
INIT_LIST_HEAD(&fs->fs_supers);
write_lock(&file_systems_lock);
p = find_filesystem(fs->name);
if (*p)
res = -EBUSY;
else
*p = fs;
write_unlock(&file_systems_lock);
return res;
}
這個結構中最關鍵的就是 read_super() 這個函式指標,它就是用於建立並設定 super_block 的目的的。
因為安裝一個檔案系統的關鍵一步就是要為“被安裝裝置”建立和設定一個 super_block,而不同的具體的檔案系統的 super_block 有自己特定的資訊,因此要求具體的檔案系統首先向核心註冊,並提供 read_super() 的實現。
5. 根據路徑名尋找目標節點的 dentry
下面來研究檔案系統中的一個非常關鍵的操作:根據路徑名尋找目標節點的 dentry。
例如要開啟 /mnt/win/dir1/abc 這個檔案,就是根據這個路徑,找到目標節點 ‘abc’ 對應的 dentry ,進而得到inode 的過程。
5.1. 尋找過程
尋找過程大致如下:
1、 <!--[endif]-->首先找到根檔案系統的根目錄節點 dentry 和 inode
2、 <!--[endif]-->由這個 inode 提供的操作介面 i_op->lookup(),找到下一層節點 ‘mnt’ 的 dentry 和 inode
3、 <!--[endif]-->由 ‘mnt’ 的 inode 找到 ‘win’ 的 dentry 和 inode
4、 <!--[endif]-->由於 ‘win’ 是個“安裝點”,因此需要找到“被安裝裝置”/dev/sda1 根目錄節點的 dentry 和inode,只要找到 vfsmount B,就可以完成這個任務。
5、 <!--[endif]-->然後由 /dev/sda1 根目錄節點的 inode 負責找到下一層節點 ‘dir1’ 的 dentry 和 inode
6、 <!--[endif]-->由於 dir1 是個“安裝點”,因此需要藉助 vfsmount C 找到 /dev/sda2 的根目錄節點 dentry 和inode
7、 <!--[endif]-->最後由這個 inode 負責找到 ‘abc’ 的 dentry 和 inode
可以看到,整個尋找過程是一個遞迴的過程。
完成尋找後,記憶體中結構如下,其中紅色線條是尋找目標節點的路徑
現在有兩個問題:
1、在尋找過程的第一步,如何得到“根檔案系統”的根目錄節點的 dentry?
答案是這個 dentry 是被儲存在程式的 task_struct 中的。後面分析程式與檔案系統關係的時候再說這個。
2、如何尋找 vfsmount B 和 C?
這是接下來要分析的。
<!--[if !supportLists]-->5.2. <!--[endif]-->vfsmount 之間的關係
我們知道, vfsmount A、B、C 之間形成了一種父子關係,為什麼不根據 A 來找到 B ,根據 B 找到 C 了?
這是因為一個檔案系統可能同時被安裝到不同的“安裝點”上。
假設把 /dev/sda1 同時安裝到 /mnt/win 和 /mnt/linux 下
現在 /mnt/win/dir1 和 /mnt/linux/dir1 對應的是同一個 dentry!!!
然後,又把 /dev/sda2 分別安裝到 /mnt/win/dir1 和 /mnt/linux/dir1 下
現在, vfsmount 與 dentry 之間的關係大致如下。可以看到:
1、現在有四個 vfsmount A, B, C, D
2、 和B對應著不同的安裝點 ‘win’ 和 ‘linux’,但是都指向 /dev/sda1 根目錄的 dentry
3、C 和D 對應著這相同的安裝點 ‘dir1’,也都指向 /dev/sda2 根目錄的 dentry
4、 C 是 A 的 child, A是 C 的 parent
5、 D 是 B 的 child, B 是 D 的 parent
5.3. 搜尋輔助結構 nameidata
在遞迴尋找目標節點的過程中,需要藉助一個搜尋輔助結構 nameidata,這是一個臨時結構,僅僅用在尋找目標節點的過程中。
在搜尋初始化時,建立 nameidata,其中 mnt 指向 current->fs->rootmnt,dentry 指向 current->fs->root
dentry 隨著目錄節點的深入而不斷變化;
而 mnt 則在每進入一個新的檔案系統後發生變化
以尋找 /mnt/win/dir1/abc 為例
開始的時候, mnt 指向 vfsmount A,dentry 指向根裝置的根目錄
隨後,dentry 先後指向 ‘mnt’ 和 ‘win’ 對應的 dentry
然後當尋找到 vfsmount B 後,mnt 指向了它,而 dentry 則指向了 /dev/sda1 根目錄的 dentry
有了這個結構,上一節的問題就可以得到解決了:
在尋找 /mnt/win/dir1/abc 的過程中,首先找到 A,接下來在要決定選 C 還是 D,因為是從 A 搜尋下來的, C 是A 的 child,因此選擇 C 而不是 D;同樣,如果是尋找 /mnt/linux/dir1/abc,則會依次選擇 B 和D。這就是為什麼nameidata 中要帶著一個 vfsmount 的原因。
6. >開啟檔案
<!--[if !supportLists]-->6.1. <!--[endif]-->“開啟檔案”結構 file
一個檔案每被開啟一次,就對應著一個 file 結構。
我們知道,每個檔案對應著一個 dentry 和 inode,每開啟一個檔案,只要找到對應的 dentry和 inode 不就可以了麼?為什麼還要引入這個 file 結構?
這是因為一個檔案可以被同時開啟多次,每次開啟的方式也可以不一樣。
而dentry 和 inode 只能描述一個物理的檔案,無法描述“開啟”這個概念。
因此有必要引入 file 結構,來描述一個“被開啟的檔案”。每開啟一個檔案,就建立一個 file 結構。
file 結構中包含以下資訊:
開啟這個檔案的程式的 uid,pid
開啟的方式
讀寫的方式
當前在檔案中的位置
實際上,開啟檔案的過程正是建立file, dentry, inode 之間的關聯的過程。
f_dentry |
f_op |
d_inode |
i_fop |
open |
struct file |
struct dentry |
struct inode |
struct file_operations |
read |
write |
ioctl |
llseek |
mmap |
<!--[if !supportLists]-->7. <!--[endif]-->檔案的讀寫
檔案一旦被開啟,資料結構之間的關係已經建立,後面對檔案的讀寫以及其它操作都變得很簡單。就是根據 fd 找到 file 結構,然後找到 dentry 和 inode,最後通過 inode->i_fop 中對應的函式進行具體的讀寫等操作即可。
<!--[if !supportLists]-->8. <!--[endif]-->程式與檔案系統的關聯
<!--[if !supportLists]-->8.1. <!--[endif]-->“開啟檔案”表和 files_struct結構
一個程式可以開啟多個檔案,每開啟一個檔案,建立一個 file 結構。所有的 file 結構的指標儲存在一個陣列中。而檔案描述符正是這個陣列的下標。
我記得以前剛開始學習程式設計的時候,怎麼都無法理解這個“檔案描述符”的概念。現在從核心的角度去看,就很容易明白“檔案描述符”是怎麼回事了。使用者僅僅看到一個“整數”,實際底層對應著的是 file, dentry, inode 等複雜的資料結構。
files_struct 用於管理這個“開啟檔案”表。
atomic_t count;
rwlock_t file_lock; /* Protects all the below members. Nests inside tsk->alloc_lock */
int max_fds;
int max_fdset;
int next_fd;
struct file ** fd; /* current fd array */
fd_set *close_on_exec;
fd_set *open_fds;
fd_set close_on_exec_init;
fd_set open_fds_init;
struct file * fd_array[NR_OPEN_DEFAULT];
};
其中的 fd_arrar[] 就是“開啟檔案”表。
task_struct 中通過成員 files 與 files_struct 關聯起來。
<!--[if !supportLists]-->8.2. <!--[endif]-->struct fs_struct
task_struct 中與檔案系統相關的還有另外一個成員 fs,它指向一個 fs_struct 。
atomic_t count;
rwlock_t lock;
int umask;
struct dentry * root, * pwd, * altroot;
struct vfsmount * rootmnt, * pwdmnt, * altrootmnt;
};
其中:
root 指向此程式的“根目錄”,通常就是“根檔案系統”的根目錄 dentry
pwd 指向此程式當前所在目錄的 dentry
因此,通過 task_struct->fs->root,就可以找到“根檔案系統”的根目錄 dentry,這就回答了 5.1 小節的第一個問題。
rootmnt :指向“安裝”根檔案系統時建立的那個 vfsmount
pwdmnt:指向“安裝”當前工作目錄所在檔案系統時建立的那個 vfsmount
這兩個域用於初始化 nameidata 結構。
<!--[if !supportLists]-->8.3. <!--[endif]-->程式與檔案系統的結構關係圖
下圖描述了程式與檔案系統之間的結構關係圖:
<!--[if !supportLists]-->9. <!--[endif]-->參考資料
1、《Linux 原始碼情景分析》上冊
2、Linux 2.4.30 原始碼
struct dentry *dentry;
struct vfsmount *mnt;
struct qstr last;
unsigned int flags;
int last_type;
};
{
struct inode *inode = iget4_locked(sb, ino, NULL, NULL);
if (inode && (inode->i_state & I_NEW)) {
sb->s_op->read_inode(inode);
unlock_new_inode(inode);
}
return inode;
}
相關文章
- Linux核心學習—— 1核心體系結構Linux
- Linux系統檔案學習內容多嗎?linux系統命令Linux
- 二、Linux檔案系統結構Linux
- Linux下的檔案系統結構Linux
- Linux檔案系統目錄結構Linux
- 關於學習 Linux 系統結構的一些總結Linux
- 資料結構學習總結--圖資料結構
- [資料庫系統]儲存和檔案結構資料庫
- 關於學習-Linux-系統結構的一些總結Linux
- 在電腦科學和作業系統中,檔案控制塊(FCB)和檔案描述符(File Descriptor)是兩種關鍵的資料結構;目錄條目(Directory Entry)作為檔案系統中的基本資料結構;作業系統資料結構
- Linux 核心101:程式資料結構Linux資料結構
- 資料結構學習筆記1資料結構筆記
- PostgreSQL 資料庫學習 - 1.資料庫體系結構之儲存結構SQL資料庫
- Linux系統中有哪些比較重要的檔案系統結構?Linux
- linux檔案系統的目錄結構筆記Linux筆記
- 核心proc檔案系統與seq介面(3)---核心proc檔案底層結構淺析
- Linux學習(三)之系統目錄結構Linux
- linux核心資料結構之kfifo【轉】Linux資料結構
- i.MX6ULL終結者Linux檔案系統的構建BusyBox構建根檔案系統Linux
- 關於資料結構的學習心得資料結構
- 競拍系統設計和核心資料結構資料結構
- linux檔案目錄結構彙總!Linux學習Linux
- 物聯網學習教程—Linux 可執行檔案結構與程式結構Linux
- Linux 核心101:虛擬檔案系統的使命Linux
- Linux 學習筆記--目錄結構及檔案基本操作Linux筆記
- 『學了就忘』Linux檔案系統管理 — 57、Linux檔案系統介紹Linux
- linux系統配置及相關檔案Linux
- JVM學習--Class類檔案結構JVM
- 資料結構學習資料結構
- Linux學習之linux檔案目錄結構彙總Linux
- 檔案系統的物理結構分配
- Linux核心啟動之根檔案系統掛載Linux
- EXT4檔案系統學習(10)VFS之磁碟結構Group和superblockBloC
- EXT4檔案系統學習(9)VFS之磁碟結構inode和direntry
- PE 檔案結構圖
- 資料結構學習之樹結構資料結構
- 【資料結構】第六章學習小結--- 圖資料結構
- Linux核心檔案Linux
- Linux檔案系統Linux