目錄
1 虛擬檔案系統概述
2 底層檔案系統與VFS四大物件
磁盤與底層檔案系統例子
超級塊物件super block
索引節點物件inode
目錄項dentry
FILE檔案物件
1 虛擬檔案系統概述
Linux中“一切皆檔案”,所有檔案都放置在以 “/” 為根目錄的一個目錄樹中,所有的硬體設備也都是檔案,各有自己的檔案系統(比如對新硬碟進行格式化的時候就要給其設定一個檔案系統),但是在Linux中訪問任何型別的檔案時,不管是普通檔案還是設備檔案等,都可以統一使用open(),read(),write()這樣的系統呼叫,這就是虛擬檔案系統VFS的核心思想,在具體的檔案系統之上提供更高一層的抽象,給各種檔案系統提供統一的IO操作介面,

VFS是一個內核軟體層,主要利用四種物件來建立一種統一的檔案模型,VFS相當于定義了一種標準,只要是按照VFS標準介面開發的檔案系統都可以接入linux,按照VFS標準即表示這個底層檔案系統要有(或者支持)四大物件,當檔案系統被掛載時,所有與它有關的資訊都放入super_block結構體中,從用戶的角度看,Linux的檔案系統只有VFS,
VFS四大物件即四種記憶體內核區的結構體物件,在具體的檔案系統中有對應的資料來初始化這四種物件,所以要注意區分內核中的物件和具體檔案系統對應的磁盤資料區域:
superblock(超級塊):存盤檔案系統的元資料,一個超級塊對應一個實際的檔案系統,
inode(索引節點):存盤磁盤上檔案的元資料
dentry(目錄項):保存檔案名與inode之間的映射關系,以及目錄與目錄下檔案之間的映射關系,是一種邏輯上的概念,有快取機制,
file:一個行程打開一個檔案時會創建一個FILE結構體,它與具體的行程和用戶關聯,需要注意的是FILE物件與行程相關聯,但是并不意味著FILE物件是某個行程專屬的,即不同行程檔案描述表中的FILE*指標可以指向相同的FILE物件,
2 底層檔案系統與VFS四大物件
磁盤與底層檔案系統例子
磁盤(disk):一個硬體設備,比如我們平時買的硬碟,可以分成很多個磁區,
磁區(partition):從磁盤上劃出來的一塊空間,類似windows中可以分CDE區,通常情況下,一個磁區只能格式化為一個檔案系統(雖然現在有新技術將一個磁區格式化為多個檔案系統),但我們通常可以認為一個磁區,
扇區(sector):硬體上I/O操作的最小單位,通常是512位元組,
塊(block):Linux虛擬檔案系統I/O操作的最小單位,作業系統在讀取硬碟的時候,一次性讀取的最小單位是一個塊,一般是4KB的大小,,
塊組(block group):一個磁區可以分成好幾個塊組,如下圖所示,同一個磁區內的塊組通常是屬于同一個檔案系統,
以下概念均是底層檔案系統劃分的資料區域,并非VFS記憶體中四大物件,但是要用這些資訊來初始化四大物件,且對四大物件的改變最終要回寫回這些區域:
引導塊(boot sector):一個磁區由引導塊和底層檔案系統組成,但是一般只有根檔案系統中這一塊有引導代碼,其它檔案系統中這一塊空著,
超級塊(super block):是一個檔案系統的核心,描述整個磁區的檔案系統資訊,在每個塊組的開頭有一份拷貝,記錄了該檔案系統的塊大小,上次掛載時間,塊組個數,每個塊組中塊的個數,以及空閑塊數和空閑Inode數等,超級塊區域的資料用于初始化VFS的超級塊物件,同樣的,VFS超級塊物件可以改變之后回寫回磁盤超級塊區域,
塊組描述符表(GDT):存盤當前塊組的全域資訊,比如邏輯塊位圖的塊號,i節點位圖的塊號,以及空閑的塊數和空閑的inode數等,
邏輯塊位圖(盤塊位圖Block Bitmap):如果某一位為1,則這個位對應的邏輯塊或者說資料區的那個塊是被使用了的,否則是空閑的,其本身只占一個塊,
索引節點位圖(inode Bitmap):如果某一位為1,則代表這一位對應的 inode 表中那個區域沒有存資訊,也是占一個塊,從這里也可以看出inode的數目是有限的,如果有非常多的小檔案的話可能造成一種情況,磁盤還有空間,inode號碼用完了,
索引節點表(inode table):inode表中每個inode節點(即索引節點)對應一個磁盤檔案,存盤了磁盤文的元資訊,即除了檔案名以外的所有資訊,比如用戶組,權限,大小,修改時間等,inode中還保存了指向檔案實際陣列的地址資訊,通過inode機制實作了檔案資料本身和資料描述資訊的分開存盤,
資料區(Data Blocks):這里存盤的就是實際的磁盤資料了,對于普通檔案,其內容就是檔案資料,對于目錄檔案,其內容是該目錄下的所有檔案名(包塊子目錄),對于軟鏈接,其內容是原目標路徑名,
超級塊物件super block
一個超級塊物件對應一個具體的檔案系統,超級塊在記憶體中的物體是一個結構體 struct super_block,超級塊實作了對具體的檔案系統的一種抽象,即不管底層是什么檔案系統,在VFS中處理的時候都將其抽象為一個 super_block結構體物件,超級塊是一個全域的資料結構,這里我們所說的超級塊都是只存在于記憶體中的資料結構,
具體地,內核中檔案系統部分呼叫alloc_super()函式從磁盤中讀取底層檔案系統超級塊區域,然后用其中的資料來給記憶體中超級塊物件賦值,
struct super_block {
struct list_head s_list; /* 系統將所有檔案系統的超級塊組成鏈表*/
dev_t s_dev; /* search index; _not_ kdev_t */
unsigned long s_blocksize;
unsigned char s_blocksize_bits;
unsigned char s_dirt;
unsigned long long s_maxbytes; /* Max file size */
struct file_system_type *s_type; //檔案系統型別
const struct super_operations *s_op; //操作函式集
struct dquot_operations *dq_op;
struct quotactl_ops *s_qcop;
struct export_operations *s_export_op;
unsigned long s_flags;
unsigned long s_magic;
struct dentry *s_root; //掛載根目錄
struct rw_semaphore s_umount;
struct mutex s_lock;
int s_count;
int s_syncing;
int s_need_sync_fs;
atomic_t s_active;
#ifdef CONFIG_SECURITY
void *s_security; //LSM框架的安全域
#endif
struct xattr_handler **s_xattr;
struct list_head s_inodes; /* 所有的inode節點鏈表*/
struct list_head s_dirty; /* dirty inodes */
struct list_head s_io; /* parked for writeback */
struct hlist_head s_anon; /* anonymous dentries for (nfs) exporting */
struct list_head s_files;
struct block_device *s_bdev;
struct list_head s_instances;
struct quota_info s_dquot; /* Diskquota specific options */
int s_frozen;
wait_queue_head_t s_wait_unfrozen;
char s_id[32];/* Informational name */
void *s_fs_info; /* Filesystem private info */
/*
* The next field is for VFS *only*. No filesystems have any business
* even looking at it. You had been warned.
*/
struct mutex s_vfs_rename_mutex; /* Kludge */
/* Granularity of c/m/atime in ns.
Cannot be worse than a second */
u32 s_time_gran;
};
s_list:是一個list_head結構體物件,list_head結構體如下定義,內核中使用一個雙向環形鏈表將所有超級塊連起來管理,即每個超級塊的s_list屬性都包含了指向內核鏈表中前一個元素和后一個元素的指標,全域變數super_blocks用于指向鏈表中第一個元素,Linux內核經常用該物件間接定義雙向回圈鏈表來管理資料,具體使用方法可以參考linux內核鏈表list_head的原理與使用_不休的博客-CSDN博客
struct{
list_head *prev;
list_head *next;
}
s_blocksize:檔案系統中資料塊大小,單位是位元組
s_dirt:臟位,在具體的硬體設備中有關于其檔案系統的資料,將設備掛載以后,會用其關于檔案系統的資料來初始化記憶體中的super_block結構體物件,而VFS是允許對超級塊物件進行修改的,修改后的資料最終是要寫回磁盤對應區域的,s_dirt用于判斷超級塊物件中資料是否臟了即被修改過了,即與磁盤上的超級塊區域是否一致,
s_dirty:臟inode的雙向回圈鏈表,用于同步記憶體資料和底層存盤介質,當我們在用戶去用open打開一個檔案,記憶體中會創建dentry和inode,當我們用write往檔案中寫入資料,則該inode臟了,將其加入到s_dirty鏈表
s_files:該超級塊表是的檔案系統中所有被打開的檔案,
s_type:是指向file_system_type型別的指標,file_system_type結構體用于保存具體的檔案系統的資訊,
s_op:super_operations結構體型別的指標,因為一個超級塊對應一種檔案系統,而每種檔案系統的操作函式可能是不同的,super_operations結構體由一些函式指標組成,這些函式指標用特定檔案系統的超級塊區域操作函式來初始化,比如里邊會有函式實作獲取和回傳呼層檔案系統inode的方法,
s_inodes:是一個list_head結構體物件,指向超級塊對應檔案系統中的所有inode索引節點的鏈表,
索引節點物件inode
保存檔案元資料,即檔案大小,設備識別符號,用戶識別符號,用戶組識別符號,檔案模式,擴展屬性,檔案讀取或修改的時間戳,鏈接數量,指向存盤該內容的磁盤區塊的指標,通過索引節點inode可以找到檔案的具體資料在磁盤中的位置,inode有兩種,一種是VFS的inode物件,一種是具體檔案系統的inode磁盤區域,前者在記憶體中,后者在磁盤中,所以每次其實是將磁盤中的inode讀取之后初始化記憶體中的inode,這樣才是算使用了磁盤檔案inode,inode是唯一的,一個inode物件了磁盤上的一個真實檔案,
<span style="font-size:14px;">440 struct inode {
441 struct list_head i_hash;
442 struct list_head i_list;
443 struct list_head i_dentry;
444
445 struct list_head i_dirty_buffers;
446 struct list_head i_dirty_data_buffers;
447
448 unsigned long i_ino;
449 atomic_t i_count;
450 kdev_t i_dev;
451 umode_t i_mode;
452 unsigned int i_nlink;
453 uid_t i_uid;
454 gid_t i_gid;
455 kdev_t i_rdev;
456 loff_t i_size;
457 time_t i_atime;
458 time_t i_mtime;
459 time_t i_ctime;
460 unsigned int i_blkbits;
461 unsigned long i_blksize;
462 unsigned long i_blocks;
463 unsigned long i_version;
464 unsigned short i_bytes;
465 struct semaphore i_sem;
466 struct rw_semaphore i_alloc_sem;
467 struct semaphore i_zombie;
468 struct inode_operations *i_op;
469 struct file_operations *i_fop; /* former ->i_op->default_file_ops */
470 struct super_block *i_sb;
471 wait_queue_head_t i_wait;
472 struct file_lock *i_flock;
473 struct address_space *i_mapping;
474 struct address_space i_data;
475 struct dquot *i_dquot[MAXQUOTAS];
476 /* These three should probably be a union */
477 struct list_head i_devices;
478 struct pipe_inode_info *i_pipe;
479 struct block_device *i_bdev;
480 struct char_device *i_cdev;
481
482 unsigned long i_dnotify_mask; /* Directory notify events */
483 struct dnotify_struct *i_dnotify; /* for directory notifications */
484
485 unsigned long i_state;
486
487 unsigned int i_flags;
488 unsigned char i_sock;
489
490 atomic_t i_writecount;
491 unsigned int i_attr_flags;
492 __u32 i_generation;
493 union {
494 struct minix_inode_info minix_i;
495 struct ext2_inode_info ext2_i;
496 struct ext3_inode_info ext3_i;
497 struct hpfs_inode_info hpfs_i;
498 struct ntfs_inode_info ntfs_i;
499 struct msdos_inode_info msdos_i;
500 struct umsdos_inode_info umsdos_i;
501 struct iso_inode_info isofs_i;
502 struct nfs_inode_info nfs_i;
503 struct sysv_inode_info sysv_i;
504 struct affs_inode_info affs_i;
505 struct ufs_inode_info ufs_i;
506 struct efs_inode_info efs_i;
507 struct romfs_inode_info romfs_i;
508 struct shmem_inode_info shmem_i;
509 struct coda_inode_info coda_i;
510 struct smb_inode_info smbfs_i;
511 struct hfs_inode_info hfs_i;
512 struct adfs_inode_info adfs_i;
513 struct qnx4_inode_info qnx4_i;
514 struct reiserfs_inode_info reiserfs_i;
515 struct bfs_inode_info bfs_i;
516 struct udf_inode_info udf_i;
517 struct ncp_inode_info ncpfs_i;
518 struct proc_inode_info proc_i;
519 struct socket socket_i;
520 struct usbdev_inode_info usbdev_i;
521 struct jffs2_inode_info jffs2_i;
522 void *generic_ip;
523 } u;
524 };</span>
以上是通用的inode物件結構體定義,
i_no:便是inode的唯一性編號
i_count:訪問該inode結構體物件的行程數
i_nlink:硬鏈接計數,等于0時將檔案從磁盤移除,
i_hash:指向哈希鏈表指標,用于查詢,已經inode號碼和對應超級塊的時候,通過哈希表來快速查詢地址,具體看下邊管理inode節點,也是list_head型別物件,這種物件就對應了一個雙向回圈鏈表,
i_dentry:指向目錄項鏈表指標,因為一個inode可以物件多個dentry,因此用一個鏈表將于本inode關聯的目錄項都連在一起,
i_op:索引節點操作函式指標,指向了inode_operation結構體,提供與inode相關的操作
i_fop:指向file_operations結構提供檔案操作,在file結構體中也有指向file_operations結構的指標,
i_sb:inode所屬檔案系統的超級塊指標
管理inode節點的四個鏈表(前兩個是全域鏈表,第三個在超級塊中):
inode_unused:目前未被使用的inode節點鏈表,即尚在記憶體中沒有銷毀,但是沒有行程使用,i_count為0,
inode_in_use:當前正在使用的inode鏈表,i_count > 0且 i_nlink > 0
super_block中的s_dirty:將所有修改過的inode鏈接起來
inode_hashtable:為了加快查找效率,將正在使用的和臟的inode放入一個哈希表中,但是不同的inode的哈希值可能相等,hash值相等的inode哦那個過i_hash成員連接,
注意是所有位于記憶體中的inode會存放在一個名為inode_hashtable的全域哈希表中,如果inode還在磁盤,沒有快取到記憶體,則不會加入全域哈希表,inode_hashtable加快了對索引節點物件的搜索,但前提是要知道inode號碼和對應的超級塊物件,
注意在inode_hashtable哈希表中的元素是鏈表,是通過inode物件中的i_hash成員鏈接起來的雙向回圈鏈表,在這個子鏈表中對應的inode的哈希值是相等的,即inode_hashtable本質是一個資料和鏈表的結合體,
目錄項dentry
與超級塊物件super block和索引節點物件inode不同,dentry在底層磁盤上并沒有物件的物體,dentry是只存在于記憶體中的物件,其本質是快取了磁盤檔案查找的結果,即dentry中包含了檔案名到inode結構體地址的映射,所以也叫“dcache”,而VFS中的超級塊物件和索引節點物件一是為了為不同底層檔案系統提供更高層的表示,二是為了提高效率,所以其在記憶體和底層磁盤有具體對應的資料,
目錄項與目錄檔案是不同的,目錄檔案是磁盤上的具體的一種檔案,目錄項則是在記憶體用于記錄檔案名和inode節點映射關系的一種資料結構,可以說是為了提高查找效率而存在的,首先先要搞懂目錄是什么,
簡而言之,目錄的本質就是一個表,存盤了該目錄下檔案的檔案名和對應的inode編號,從inode table中找到對應編號的inode,就可以找到對應的資料,而目錄項dentry本質上是在查找檔案程序中快取在記憶體中的物件,所以目錄項也被稱為目錄項快取dcache,其與目錄檔案并不是一種像記憶體中與磁盤上的超級塊,索引節點那樣的對應關系,
也有些地方將目錄檔案中代表該目錄下一個檔案的表項叫做目錄項,其實也沒錯,知道其與記憶體中dentry的區別即可,其實也可以理解為磁盤上超級塊與記憶體中超級塊之間的關系,
舉個例子,如下圖中所示目錄結構:

對于CPP這個目錄檔案,其存盤的資料內容是:
| 檔案名 | inode編號(只是舉例,不代表真實情況) |
| . | 1 |
| .. | 2 |
| Calc | 3 |
| test | 4 |
| webserver | 5 |
即對于目錄檔案CPP來說,其內容是目錄下檔案名與其對應inode編號的映射,目錄檔案CPP下一共有五個檔案,都是目錄檔案,其中“.”是當前目錄檔案的硬鏈接,即其與CPP關聯的是同一個inode,“..”是上一級目錄的硬鏈接,
![]()
如圖中所示,CPP目錄檔案的硬鏈接數(下文中講解)是5,目錄檔案CPP本身,CPP目錄下的 “.”,以及目錄檔案Calc,test,webserver這三個目錄下的“..”,這五個目錄檔案互為硬鏈接,因此硬鏈接數是5,因為一個目錄下一定含有其本身的硬鏈接,這也就是為什么目錄檔案的硬鏈接數至少為2,
而對于目錄檔案test來說,其資料內容是:
| 檔案名 | inode編號 |
| . | 4 |
| .. | 1 |
| makefile | 6 |
| test.c | 7 |
| test.o | 8 |
| welove | 9 |
test目錄下包括了當前目錄檔案的inode編號以及上級目錄即CPP的inode編號,以及三個普通檔案與其inode編號的映射,
現在假設我們要查找 ../CPP/test/test.c 這個檔案,則步驟為(假設已知CPP的inode編號,胡羅CPP之前檔案的查找程序):
由CPP的inode編號找到檔案名CPP對應的索引節點,由索引節點找到對應資料塊,從資料塊中取出CPP檔案的內容,即上文中目錄檔案表1,
從目錄檔案表1找到 test 對應的inode編號,然后找到對應索引節點以及資料塊,從資料塊中取出目錄檔案test的內容,即目錄檔案表2,
從目錄檔案表2中找到test.c對應的inode編號,從而找到test.c在磁盤上的資料塊,取出內容,
這樣一個查找檔案的程序涉及大量的磁盤操作,由于磁盤相比記憶體速度很慢,因此產生了dentry的快取機制,即每次VFS訪問底層資料時,都將訪問結果保存下來,即在記憶體中構造dentry結構來保存檔案名(d_name)和inode號碼對應的索引節點的地址之間的映射,并且,當查找一個檔案時,查找到其路徑上的每個檔案時候在記憶體中都會構造一個dentry結構體物件,并通過一定的機制(其實就是定義各種鏈表來間接形成一個樹形層次結構以便于管理)來形成檔案系統的目錄樹結構,
對于上文中查找test.c檔案的例子來說,CPP,test,test.c,搜索到路徑上的這三個檔案時候都會建立相應的dentry,并且這三個檔案的目錄層次關系也會保留,
即dentry或者說dcache的目的有兩個,一是保存訪問到的檔案名與索引節點地址間的映射關系,二是維護好檔案之間的目錄層次關系,dentry結構體定義如下:
struct dentry {
atomic_t d_count;//目錄項物件參考計數器
unsigned int d_flags; /* protected by d_lock */
spinlock_t d_lock; /* per dentry lock */
struct inode *d_inode; /* Where the name belongs to - NULL is
struct hlist_node d_hash; /* 鏈接到dentry cache的hash鏈表 */
struct dentry *d_parent; /* 指向父dentry結構的指標 */
struct qstr d_name;//檔案名
struct list_head d_lru; /* LRU list */
union {
struct list_head d_child; /* child of parent list */
struct rcu_head d_rcu;
} d_u;
struct list_head d_subdirs; /* 是子項的鏈表頭,子項可能是目錄也可能是檔案,所有子項都要鏈接到這個鏈表, */
struct list_head d_alias; /* inode alias list */
unsigned long d_time; /* used by d_revalidate */
struct dentry_operations *d_op;
struct super_block *d_sb; /* The root of the dentry tree */
void *d_fsdata; /* fs-specific data */
int d_mounted; /*表示dentry是否是一個掛載點,如果是掛載點,該成員不為0
unsigned char d_iname[DNAME_INLINE_LEN_MIN]; /* small names */
};
d_count:目錄項物件參考計數器
d_name:檔案名
d_inode:inode節點的指標,便于快速找到對應的索引節點
d_sb:指向對應超級塊的指標
d_op:指向dentry對應的操作函式集
d_subdirs & d_child:某目錄的d_subdirs與該目錄下所有檔案的d_child成員一起形成一個雙向回圈鏈表,將該目錄下的所有檔案連接在一起,目的是保留檔案的目錄結構,即一個d_subdirs和多個d_child一起形成鏈表,d_subdirs對應檔案在d_child對應檔案的上一層目錄,具體可見下圖,
d_parent:指向父目錄的dentry物件,具體看下圖,
通過d_subdirs和d_child把同一目錄下的檔案都連接了起來形成鏈表,然后通過d_parent成員可以確定該鏈表對應的檔案所在的目錄,這三個成員一起就能完全保留檔案之間的目錄層次關系,比如當移動檔案的時候,只需要將dentry物件從舊得父dentry鏈表(d_subdirs)上脫離,鏈接到新的父dentry的d_subdirs鏈表上,并將d_parent成員指向新的父dentry即可,可以看到移動檔案并沒有移動底層檔案,甚至沒有改變inode,只是改變了快取中的dentry(最終改變目錄檔案),因此在同一個檔案系統中移動檔案會很快,但是跨檔案系統就會改變inode和底層資料區了,因此速度很慢,

dentry_hashtable & d_hash:dentry_table哈希表維護在記憶體中的所有目錄項,哈希表中每個元素都是一個雙向回圈鏈表,用于維護哈希值相等的目錄項,這個雙向回圈鏈表是通過dentry中的d_hash成員來鏈接在一起的,
FILE檔案物件
是VFS四大物件里唯一一個行程級物件,即與具體的行程相關聯,與行程相關聯的意思是,某個行程打開一個檔案,內核就會創建一個FILE物件,但是并不意味著這個FILE物件是這個行程專屬的,不同行程中的檔案描述符表中的指標可以指向相同的FILE物件(行程間通信?),從而共享這個物件,具體看下圖:

每個行程的行程控制塊PCB中都有一個檔案描述符表,檔案描述符表的索引就是所謂的檔案描述符,在系統呼叫層面,都是用檔案描述符來進行各種IO操作,這個表的默認大小是1024,行程每次呼叫open打開一個檔案的時候,內核就會創建一個FILE物件,然后把檔案描述符表中最小可用的索引號給這個FILE物件,并用一個FILE*指標作為檔案描述符表對應位置的表項,來指向代表打開檔案的這個FILE物件,系統級的打開檔案表是由所有內核創建的FILE物件通過其fu_list成員連接成的雙向回圈鏈表,即系統集打開檔案表中的表項就是FILE物件,FILE物件中有目錄項成員dentry,dentry中有指向inode索引節點的指標,即通過打開檔案表中的表項,就能找到對應的inode,注意上圖打開檔案表中把目錄項和inode指標列了兩列,實際上inode指標應該是目錄項中的成員,
行程A中,
檔案描述符0和1指向同一個檔案表項,可能是用了dup函式進行檔案描述符重定向
檔案描述2和0&1最終指向了同一個inode,即同一個底層檔案,原因應該是用open重復打開了同一個檔案,因此雖然檔案描述符2和 0&1 有不同的檔案表項,但是最終指向的檔案是一樣的,因為每呼叫一次open,內核就會創建一個FILE物件,不同的FILE物件雖然可以對應同一個磁盤檔案,但是其讀寫位置等資訊可以不一樣,
行程A中的檔案描述符012最終指向檔案與行程B中檔案描述符0指向檔案是一樣的,因為在不同的行程中用open打開了同一個檔案,
實際上,還存在下圖中的情況,不同行程中的檔案描述符項指向了同一個打開檔案表的檔案表項,下圖中行程A和B的檔案描述符2指向了同一個打開檔案表項,可能是因為用fork創建了子行程,子行程會與父行程共享檔案描述符表,
struct file {
union {
struct list_head fu_list; //檔案物件鏈表指標linux/include/linux/list.h
struct rcu_head fu_rcuhead; //RCU(Read-Copy Update)是Linux 2.6內核中新的鎖機制
} f_u;
struct path f_path; //包含dentry和mnt兩個成員,用于確定檔案路徑
#define f_dentry f_path.dentry //f_path的成員之一,當前檔案的dentry結構
#define f_vfsmnt f_path.mnt //表示當前檔案所在檔案系統的掛載根目錄
const struct file_operations *f_op; //與該檔案相關聯的操作函式
atomic_t f_count; //檔案的參考計數(有多少行程打開該檔案)
unsigned int f_flags; //對應于open時指定的flag
mode_t f_mode; //讀寫模式:open的mod_t mode引數
loff_t f_pos;//當前檔案指標位置
off_t f_pos; //該檔案在當前行程中的檔案偏移量
struct fown_struct f_owner; //該結構的作用是通過信號進行I/O時間通知的資料,
unsigned int f_uid, f_gid;// 檔案所有者id,所有者組id
struct file_ra_state f_ra; //在linux/include/linux/fs.h中定義,檔案預讀相關
unsigned long f_version;//記錄檔案的版本號,每次使用之后遞增
#ifdef CONFIG_SECURITY
void *f_security;
#endif
/* needed for tty driver, and maybe others */
void *private_data;//使用這個成員來指向分配的資料
#ifdef CONFIG_EPOLL
/* Used by fs/eventpoll.c to link all the hooks to this file */
struct list_head f_ep_links;
spinlock_t f_ep_lock;
#endif /* #ifdef CONFIG_EPOLL */
struct address_space *f_mapping;
};
fu_list:用于將所有的打開的FILE檔案連接起來形成打開檔案表
f_path:里邊包含目錄項dentry和掛載點資訊
f_op:指向與檔案相關的操作合集,比如read,write,lseek等
f_count:指示有多少個行程在使用該檔案,這里也能看出FILE并不是某個行程的私有物件
f_flags:打開檔案時候指定的標志,對應open的flags引數,
f_pos:檔案的讀寫位置
行程與VFS四大物件聯系示意圖,這幾個東西都是在內核空間中的

一個目錄項dentry可以對應多個FILE(比如多次打開同一檔案),一個inode可以對應多個dentry(比如打開多個互為硬鏈接的檔案),inode與底層檔案之間是一對一的關系,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/287440.html
標籤:區塊鏈
上一篇:智能合約實戰 solidity 語法學習 08 [ require assert modifier revert ]
