參考鏈接如下:
https://www.ibm.com/developerworks/cn/linux/l-cn-hardandsymb-links/#fig2
https://blog.csdn.net/Ohmyberry/article/details/80427492
https://blog.csdn.net/shanshanpt/article/details/38943731
https://www.cnblogs.com/LittleHann/p/3865490.html
https://www.cnblogs.com/LittleHann/p/4305892.html
https://www.cnblogs.com/fengkang1008/p/4691231.html
https://www.cnblogs.com/linux-xin/p/8126999.html
1. 圖示文件系統
1.1經典的文件系統
需要注意幾點:
- dentry 本身目錄塊也屬於數據塊
- inode指向實際block,其屬於i節點區
- 目錄塊本身包含inode指針和文件名
1.2 對文件系統結構體分析
精力有限,湊合看看,結構體很長,選了一些,例如:
標準IO的fread函數, size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream) ,實際調用read,其中需要注意FILE 與 fd 的差異,fd爲fd_array的數組索引號,實際存放file指針,通過file指針調用f_op的函數
2. VFS
如下兩張圖能直觀反映Linux下文件系統中VFS所起的作用,上接系統調用,下接真實文件系統,類似於autosar裏的RTE,使得不同的文件系統在Linux裏共存,且同一個API可以完成對文件系統的操作。
VFS抽象了文件、目錄項 (dentry)、索引節點 (inode) 及掛載點,其主要有如下四個部分組成:
- 超級塊對象 (superblock object)
- 索引節點對象 (inode object)、
- 目錄項對象 (dentry object) 、
- 文件對象 (file object)
上述四個模塊在VFS中聯繫如下圖所示:
2.1 superblock
超級塊就是對所有文件系統的管理機構,主要管理block和inode等,記錄保存文件系統的類型、大小、狀態等等,每種文件系統都要把自己的信息掛到super_blocks這麼一個全局鏈表上,超級塊與文件系統一一對應,同一個文件系統類型可能含有多個超級塊。
- block 與inode 的總量;
- 未使用與已使用的inode / block 數量;
- block 與inode 的大小(block 爲1, 2, 4K,inode 爲128bytes 或256bytes);
- filesystem 的掛載時間、最近一次寫入資料的時間、最近一次檢驗磁盤(fsck) 的時間等檔案系統的相關信息;
- 一個valid bit 數值,若此檔案系統已被掛載,則valid bit 爲0 ,若未被掛載,則valid bit 爲1 。
“文件系統”和“文件系統類型”不一樣!一個文件系統類型下可以包括很多文件系統即很多的super_block
具體代碼和解釋部分,參考https://www.cnblogs.com/LittleHann/p/3865490.html
struct super_block
{
/*
Keep this first
指向超級塊鏈表的指針,用於將系統中所有的超級塊聚集到一個鏈表中,該鏈表的表頭是全局變量super_blocks
*/
struct list_head s_list;
/*
search index; _not_ kdev_t
設備標識符
*/
dev_t s_dev;
//以字節爲單位的塊大小
unsigned long s_blocksize;
//以位爲單位的塊大小
unsigned char s_blocksize_bits;
//修改髒標誌,如果以任何方式改變了超級塊,需要向磁盤迴寫,都會將s_dirt設置爲1,否則爲0
unsigned char s_dirt;
//文件大小上限 Max file size
loff_t s_maxbytes;
//文件系統類型
struct file_system_type *s_type;
/*
struct super_operations
{
//給定的超級塊下創建和初始化一個新的索引節點對象;
struct inode *(*alloc_inode)(struct super_block *sb);
//用於釋放給定的索引節點;
void (*destroy_inode)(struct inode *);
//VFS在索引節點髒(被修改)時會調用此函數,日誌文件系統(如ext3,ext4)執行該函數進行日誌更新;
void (*dirty_inode) (struct inode *);
//用於將給定的索引節點寫入磁盤,wait參數指明寫操作是否需要同步;
int (*write_inode) (struct inode *, struct writeback_control *wbc);
//在最後一個指向索引節點的引用被釋放後,VFS會調用該函數,VFS只需要簡單地刪除這個索引節點後,普通Uinx文件系統就不會定義這個函數了;
void (*drop_inode) (struct inode *);
//用於從磁盤上刪除給定的索引節點;
void (*delete_inode) (struct inode *);
//在卸載文件系統時由VFS調用,用來釋放超級塊,調用者必須一直持有s_lock鎖;
void (*put_super) (struct super_block *);
//用給定的超級塊更新磁盤上的超級塊。VFS通過該函數對內存中的超級塊和磁盤中的超級塊進行同步。調用者必須一直持有s_lock鎖;
void (*write_super) (struct super_block *);
//使文件系統的數據元與磁盤上的文件系統同步。wait參數指定操作是否同步;
int (*sync_fs)(struct super_block *sb, int wait);
int (*freeze_fs) (struct super_block *);
int (*unfreeze_fs) (struct super_block *);
//VFS通過調用該函數獲取文件系統狀態。指定文件系統縣官的統計信息將放置在statfs中;
int (*statfs) (struct dentry *, struct kstatfs *);
//當指定新的安裝選項重新安裝文件系統時,VFS會調用該函數。調用者必須一直持有s_lock鎖;
int (*remount_fs) (struct super_block *, int *, char *);
//VFS調用該函數釋放索引節點,並清空包含相關數據的所有頁面;
void (*clear_inode) (struct inode *);
//VFS調用該函數中斷安裝操作。該函數被網絡文件系統使用,如NFS;
void (*umount_begin) (struct super_block *);
int (*show_options)(struct seq_file *, struct vfsmount *);
int (*show_stats)(struct seq_file *, struct vfsmount *);
#ifdef CONFIG_QUOTA
ssize_t (*quota_read)(struct super_block *,
int, char *, size_t, loff_t);
ssize_t (*quota_write)(struct super_block *,
int, const char *, size_t, loff_t);
#endif
int (*bdev_try_to_free_page)(struct super_block*,
struct page*, gfp_t);
};
*/
const struct super_operations *s_op;
//磁盤限額方法
const struct dquot_operations *dq_op;
//磁盤限額方法
const struct quotactl_ops *s_qcop;
//導出方法
const struct export_operations *s_export_op;
//掛載標誌
unsigned long s_flags;
//文件系統魔數
unsigned long s_magic;
//目錄掛載點,s_root將超級塊與全局根目錄的dentry項關聯起來,只有通常可見的文件系統的超級塊,才指向/(根)目錄的dentry實例。具有特殊功能、不出現在通常的目錄層次結構中的文件系統(例如管道或套接字文件系統),指向專門的項,不能通過普通的文件命令訪問。處理文件系統對象的代碼經常需要檢查文件系統是否已經裝載,而s_root可用於該目的,如果它爲NULL,則該文件系統是一個僞文件系統,只在內核內部可見。否則,該文件系統在用戶空間中是可見的
struct dentry *s_root;
//卸載信號量
struct rw_semaphore s_umount;
//超級塊信號量
struct mutex s_lock;
//引用計數
int s_count;
//尚未同步標誌
int s_need_sync;
//活動引用計數
atomic_t s_active;
#ifdef CONFIG_SECURITY
//安全模塊
void *s_security;
#endif
struct xattr_handler **s_xattr;
//all inodes
struct list_head s_inodes;
//匿名目錄項 anonymous dentries for (nfs) exporting
struct hlist_head s_anon;
//被分配文件鏈表,列出了該超級塊表示的文件系統上所有打開的文件。內核在卸載文件系統時將參考該列表,如果其中仍然包含爲寫入而打開的文件,則文件系統仍然處於使用中,卸載操作失敗,並將返回適當的錯誤信息
struct list_head s_files;
/* s_dentry_lru and s_nr_dentry_unused are protected by dcache_lock */
struct list_head s_dentry_lru;
//unused dentry lru of dentry on lru
int s_nr_dentry_unused;
//指向了底層文件系統的數據所在的相關塊設備
struct block_device *s_bdev;
struct backing_dev_info *s_bdi;
struct mtd_info *s_mtd;
//該類型文件系統
struct list_head s_instances;
//限額相關選項 Diskquota specific options
struct quota_info s_dquot;
int s_frozen;
wait_queue_head_t s_wait_unfrozen;
//文本名字 Informational name
char s_id[32];
//Filesystem private info
void *s_fs_info;
fmode_t s_mode;
/*
* 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;
/*
* Filesystem subtype. If non-empty the filesystem type field
* in /proc/mounts will be "type.subtype"
*/
char *s_subtype;
/*
* Saved mount options for lazy filesystems using
* generic_show_options()
*/
char *s_options;
};
2.2 inode
inode保存着文件大小,設備標識符,用戶標識符,用戶組標識符,文件模式,擴展屬性,文件讀取或修改的時間戳,鏈接數量,指向存儲該內容的磁盤區塊的指針,文件分類等等。
inode分磁盤inode和vfs內存inode,內核會把磁盤inode讀入內存,加上點東西形成內存inode,i_links_count爲磁盤inode計數,i_nlink爲內存inode計數。
inode 號(inode 是文件元數據的一部分但其並不包含文件名,inode 號即索引節點號)是文件的唯一標識而非文件名。文件名僅是爲了方便人們的記憶和使用,系統或程序通過 inode 號尋找正確的文件數據塊。
inode需要注意兩個參數,i_count 與 i_nlink,i_count 爲引用計數,i_nlink爲硬鏈接計數,每個dentry都有一個唯一的inode,而每個inode則可能有多個dentry,由於硬鏈接是有着相同 inode 號僅文件名不同的文件,因此硬鏈接存在以下幾點特性:
- 文件有相同的 inode 及 data block;
- 只能對已存在的文件進行創建;
- 不能交叉文件系統進行硬鏈接的創建;
- 不能對目錄進行創建,只可對文件創建;
- 刪除一個硬鏈接文件並不影響其他有相同 inode 號的文件。
軟鏈接與硬鏈接不同,若文件用戶數據塊中存放的內容是另一文件的路徑名的指向,則該文件就是軟連接
struct inode
{
/*
哈希表
*/
struct hlist_node i_hash;
/*
索引節點鏈表(backing dev IO list)
*/
struct list_head i_list;
struct list_head i_sb_list;
/*
目錄項鍊表
*/
struct list_head i_dentry;
/*
節點號
*/
unsigned long i_ino;
/*
引用記數
*/
atomic_t i_count;
/*
硬鏈接數
*/
unsigned int i_nlink;
/*
使用者id
*/
uid_t i_uid;
/*
使用者所在組id
*/
gid_t i_gid;
/*
實設備標識符
*/
dev_t i_rdev;
/*
版本號
*/
u64 i_version;
/*
以字節爲單位的文件大小
*/
loff_t i_size;
#ifdef __NEED_I_SIZE_ORDERED
seqcount_t i_size_seqcount;
#endif
/*
最後訪問時間
*/
struct timespec i_atime;
/*
最後修改(modify)時間
*/
struct timespec i_mtime;
/*
最後改變(change)時間
*/
struct timespec i_ctime;
/*
文件的塊數
*/
blkcnt_t i_blocks;
/*
以位爲單位的塊大小
*/
unsigned int i_blkbits;
/*
使用的字節數
*/
unsigned short i_bytes;
/*
訪問權限控制
*/
umode_t i_mode;
/*
自旋鎖
*/
spinlock_t i_lock;
struct mutex i_mutex;
/*
索引節點信號量
*/
struct rw_semaphore i_alloc_sem;
/*
索引節點操作表
索引節點的操作inode_operations定義在linux/fs.h
struct inode_operations
{
/*
1. VFS通過系統調用create()和open()來調用該函數,從而爲dentry對象創建一個新的索引節點。在創建時使用mode制定初始模式
*/
int (*create) (struct inode *, struct dentry *,int);
/*
2. 該函數在特定目錄中尋找索引節點,該索引節點要對應於dentry中給出的文件名
*/
struct dentry * (*lookup) (struct inode *, struct dentry *);
/*
3. 該函數被系統調用link()調用,用來創建硬連接。硬鏈接名稱由dentry參數指定,連接對象是dir目錄中ld_dentry目錄想所代表的文件
*/
int (*link) (struct dentry *, struct inode *, struct dentry *);
/*
4. 該函數被系統調用unlink()調用,從目錄dir中刪除由目錄項dentry制動的索引節點對象
*/
int (*unlink) (struct inode *, struct dentry *);
/*
5. 該函數被系統調用symlik()調用,創建符號連接,該符號連接名稱由symname指定,連接對象是dir目錄中的dentry目錄項
*/
int (*symlink) (struct inode *, struct dentry *, const char *);
/*
6. 該函數被mkdir()調用,創建一個新路徑。創建時使用mode制定的初始模式
*/
int (*mkdir) (struct inode *, struct dentry *, int);
/*
7. 該函數被系統調用rmdir()調用,刪除dir目錄中的dentry目錄項代表的文件
*/
int (*rmdir) (struct inode *, struct dentry *);
/*
8. 該函數被系統調用mknod()調用,創建特殊文件(設備文件、命名管道或套接字)。要創建的文件放在dir目錄中,其目錄項問dentry,關聯的設備爲rdev,初始權限由mode指定
*/
int (*mknod) (struct inode *, struct dentry *, int, dev_t);
/*
9. VFS調用該函數來移動文件。文件源路徑在old_dir目錄中,源文件由old_dentry目錄項所指定,目標路徑在new_dir目錄中,目標文件由new_dentry指定
*/
int (*rename) (struct inode *, struct dentry *, struct inode *, struct dentry *);
/*
10. 該函數被系統調用readlink()調用,拷貝數據到特定的緩衝buffer中。拷貝的數據來自dentry指定的符號鏈接,最大拷貝大小可達到buflen字節
*/
int (*readlink) (struct dentry *, char *, int);
/*
11. 該函數由VFS調用,從一個符號連接查找他指向的索引節點,由dentry指向的連接被解析
*/
int (*follow_link) (struct dentry *, struct nameidata *);
/*
12. 在follow_link()調用之後,該函數由vfs調用進行清楚工作
*/
int (*put_link) (struct dentry *, struct nameidata *);
/*
13. 該函數由VFS調用,修改文件的大小,在調用之前,索引節點的i_size項必須被設置成預期的大小
*/
void (*truncate) (struct inode *);
/*
該函數用來檢查inode所代表的文件是否允許特定的訪問模式,如果允許特定的訪問模式,返回0,否則返回負值的錯誤碼。多數文件系統都將此區域設置爲null,使用VFS提供的通用方法進行檢查,這種檢查操作僅僅比較索引
及誒但對象中的訪問模式位是否和mask一致,比較複雜的系統, 比如支持訪問控制鏈(ACL)的文件系統,需要使用特殊的permission()方法
*/
int (*permission) (struct inode *, int);
/*
該函數被notify_change調用,在修改索引節點之後,通知發生了改變事件
*/
int (*setattr) (struct dentry *, struct iattr *);
/*
在通知索引節點需要從磁盤中更新時,VFS會調用該函數
*/
int (*getattr) (struct vfsmount *, struct dentry *, struct kstat *);
/*
該函數由VFS調用,向dentry指定的文件設置擴展屬性,屬性名爲name,值爲value
*/
int (*setxattr) (struct dentry *, const char *, const void *, size_t, int);
/*
該函數被VFS調用,向value中拷貝給定文件的擴展屬性name對應的數值
*/
ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);
/*
該函數將特定文件所有屬性別表拷貝到一個緩衝列表中
*/
ssize_t (*listxattr) (struct dentry *, char *, size_t);
/*
該函數從給定文件中刪除指定的屬性
*/
int (*removexattr) (struct dentry *, const char *);
};
*/
const struct inode_operations *i_op;
/*
默認的索引節點操作
former ->i_op->default_file_ops
*/
const struct file_operations *i_fop;
/*
相關的超級塊
*/
struct super_block *i_sb;
/*
文件鎖鏈表
*/
struct file_lock *i_flock;
/*
相關的地址映射
*/
struct address_space *i_mapping;
/*
設備地址映射
address_space結構與文件的對應:一個具體的文件在打開後,內核會在內存中爲之建立一個struct inode結構,其中的i_mapping域指向一個address_space結構。這樣,一個文件就對應一個address_space結構,一個 address_space與一個偏移量能夠確定一個page cache 或swap cache中的一個頁面。因此,當要尋址某個數據時,很容易根據給定的文件及數據在文件內的偏移量而找到相應的頁面
*/
struct address_space i_data;
#ifdef CONFIG_QUOTA
/*
節點的磁盤限額
*/
struct dquot *i_dquot[MAXQUOTAS];
#endif
/*
塊設備鏈表
*/
struct list_head i_devices;
union
{
//管道信息
struct pipe_inode_info *i_pipe;
//塊設備驅動
struct block_device *i_bdev;
struct cdev *i_cdev;
};
/*
索引節點版本號
*/
__u32 i_generation;
#ifdef CONFIG_FSNOTIFY
/*
目錄通知掩碼
all events this inode cares about
*/
__u32 i_fsnotify_mask;
struct hlist_head i_fsnotify_mark_entries; /* fsnotify mark entries */
#endif
#ifdef CONFIG_INOTIFY
struct list_head inotify_watches; /* watches on this inode */
struct mutex inotify_mutex; /* protects the watches list */
#endif
/*
狀態標誌
*/
unsigned long i_state;
/*
首次修改時間
jiffies of first dirtying
*/
unsigned long dirtied_when;
/*
文件系統標誌
*/
unsigned int i_flags;
/*
寫者記數
*/
atomic_t i_writecount;
#ifdef CONFIG_SECURITY
/*
安全模塊
*/
void *i_security;
#endif
#ifdef CONFIG_FS_POSIX_ACL
struct posix_acl *i_acl;
struct posix_acl *i_default_acl;
#endif
void *i_private; /* fs or device private pointer */
};
2.3 dentry 目錄
目錄項是描述文件的邏輯屬性,只存在於內存中,並沒有實際對應的磁盤上的描述,更確切的說是存在於內存的目錄項緩存,爲了提高查找性能而設計。注意不管是文件夾還是最終的文件,都是屬於目錄項,所有的目錄項在一起構成一顆龐大的目錄樹。例如:open一個文件/home/xxx/yyy.txt,那麼/、home、xxx、yyy.txt都是一個目錄項,VFS在查找的時候,根據一層一層的目錄項找到對應的每個目錄項的inode,那麼沿着目錄項進行操作就可以找到最終的文件。
目錄也是一種文件(所以也存在對應的inode)
struct dentry
{
//目錄項引用計數器
atomic_t d_count;
/*
目錄項標誌 protected by d_lock
#define DCACHE_AUTOFS_PENDING 0x0001 // autofs: "under construction"
#define DCACHE_NFSFS_RENAMED 0x0002 // this dentry has been "silly renamed" and has to be eleted on the last dput()
#define DCACHE_DISCONNECTED 0x0004 //指定了一個dentry當前沒有連接到超級塊的dentry樹
#define DCACHE_REFERENCED 0x0008 //Recently used, don't discard.
#define DCACHE_UNHASHED 0x0010 //該dentry實例沒有包含在任何inode的散列表中
#define DCACHE_INOTIFY_PARENT_WATCHED 0x0020 // Parent inode is watched by inotify
#define DCACHE_COOKIE 0x0040 // For use by dcookie subsystem
#define DCACHE_FSNOTIFY_PARENT_WATCHED 0x0080 // Parent inode is watched by some fsnotify listener
*/
unsigned int d_flags;
//per dentry lock
spinlock_t d_lock;
//當前dentry對象表示一個裝載點,那麼d_mounted設置爲1,否則爲0
int d_mounted;
/*
文件名所屬的inode,如果爲NULL,則表示不存在的文件名
如果dentry對象是一個不存在的文件名建立的,則d_inode爲NULL指針,這有助於加速查找不存在的文件名,通常情況下,這與查找實際存在的文件名同樣耗時
*/
struct inode *d_inode;
/*
The next three fields are touched by __d_lookup. Place them here so they all fit in a cache line.
*/
//用於查找的散列表 lookup hash list
struct hlist_node d_hash;
/*
指向當前的dentry實例的父母了的dentry實例 parent directory
當前的dentry實例即位於父目錄的d_subdirs鏈表中,對於根目錄(沒有父目錄),d_parent指向其自身的dentry實例
*/
struct dentry *d_parent;
/*
d_iname指定了文件的名稱,qstr是一個內核字符串的包裝器,它存儲了實際的char*字符串以及字符串長度和散列值,這使得更容易處理查找工作
要注意的是,這裏並不存儲絕對路徑,而是隻有路徑的最後一個分量,例如對/usr/bin/emacs只存儲emacs,因爲在linux中,路徑信息隱含在了dentry層次鏈表結構中了
*/
struct qstr d_name;
//LRU list
struct list_head d_lru;
/*
* d_child and d_rcu can share memory
*/
union
{
/* child of parent list */
struct list_head d_child;
//鏈表元素,用於將dentry連接到inode的i_dentry鏈表中
struct rcu_head d_rcu;
} d_u;
//our children 子目錄/文件的目錄項鍊表
struct list_head d_subdirs;
/*
inode alias list 鏈表元素,用於將dentry連接到inode的i_dentry鏈表中
d_alias用作鏈表元素,以連接表示相同文件的各個dentry對象,在利用硬鏈接用兩個不同名稱表示同一文件時,會發生這種情況,對應於文件的inode的i_dentry成員用作該鏈表的表頭,各個dentry對象通過d_alias連接到該鏈表中
*/
struct list_head d_alias;
//used by d_revalidate
unsigned long d_time;
/*
d_op指向一個結構,其中包含了各種函數指針,提供對dentry對象的各種操作,這些操作必須由底層文件系統實現
struct dentry_operations
{
//在把目錄項對象轉換爲一個文件路徑名之前,判定該目錄項對象是否依然有效
int (*d_revalidate)(struct dentry *, struct nameidata *);
//生成一個散列值,用於目錄項散列表
int (*d_hash) (struct dentry *, struct qstr *);
//比較兩個文件名
int (*d_compare) (struct dentry *, struct qstr *, struct qstr *);
//當對目錄項對象的最後一個引用被刪除,調用該方法
int (*d_delete)(struct dentry *);
//當要釋放一個目錄項對象時,調用該方法
void (*d_release)(struct dentry *);
//當一個目錄對象變爲負狀態時,調用該方法
void (*d_iput)(struct dentry *, struct inode *);
char *(*d_dname)(struct dentry *, char *, int);
};
*/
const struct dentry_operations *d_op;
//The root of the dentry tree dentry樹的根,超級塊
struct super_block *d_sb;
//fs-specific data 特定文件系統的數據
void *d_fsdata;
/*
短文件名small names存儲在這裏
如果文件名由少量字符組成,則只保存在d_iname中,而不是dnanme中,用於加速訪問
*/
unsigned char d_iname[DNAME_INLINE_LEN_MIN];
};
2.4 file結構
件結構體代表一個打開的文件,系統中的每個打開的文件在內核空間都有一個關聯的struct file。它由內核在打開文件時創建,並傳遞給在文件上進行操作的任何函數。在文件的所有實例都關閉後,內核釋放這個數據結構
struct file
{
/*
* fu_list becomes invalid after file_free is called and queued via
* fu_rcuhead for RCU freeing
*/
union
{
/*
定義在 linux/include/linux/list.h中
struct list_head
{
struct list_head *next, *prev;
};
用於通用文件對象鏈表的指針,所有打開的文件形成一個鏈表
*/
struct list_head fu_list;
/*
定義在linux/include/linux/rcupdate.h中
struct rcu_head
{
struct rcu_head *next;
void (*func)(struct rcu_head *head);
};
RCU(Read-Copy Update)是Linux 2.6內核中新的鎖機制
*/
struct rcu_head fu_rcuhead;
} f_u;
/*
定義在linux/include/linux/namei.h中
struct path
{
/*
struct vfsmount *mnt的作用是指出該文件的已安裝的文件系統,即指向VFS安裝點的指針
*/
struct vfsmount *mnt;
/*
struct dentry *dentry是與文件相關的目錄項對象,指向相關目錄項的指針
*/
struct dentry *dentry;
};
*/
struct path f_path;
#define f_dentry f_path.dentry
#define f_vfsmnt f_path.mnt
/*
指向文件操作表的指針
定義在linux/include/linux/fs.h中,其中包含着與文件關聯的操作,例如
struct file_operations
{
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, 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);
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *, fl_owner_t id);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, struct dentry *, int datasync);
int (*aio_fsync) (struct kiocb *, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
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);
int (*check_flags)(int);
int (*flock) (struct file *, int, struct file_lock *);
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
int (*setlease)(struct file *, long, struct file_lock **);
};
當打開一個文件時,內核就創建一個與該文件相關聯的struct file結構,其中的*f_op就指向的是具體對該文件進行操作的函數
例如用戶調用系統調用read來讀取該文件的內容時,那麼系統調用read最終會陷入內核調用sys_read函數,而sys_read最終會調用於該文件關聯的struct file結構中的f_op->read函數對文件內容進行讀取
*/
const struct file_operations *f_op;
spinlock_t f_lock; /* f_ep_links, f_flags, no IRQ */
/*
typedef struct { volatile int counter; } atomic_t;
volatile修飾字段告訴gcc不要對該類型的數據做優化處理,對它的訪問都是對內存的訪問,而不是對寄存器的訪問
f_count的作用是記錄對文件對象的引用計數,也即當前有多少個進程在使用該文件
*/
atomic_long_t f_count;
/*
當打開文件時指定的標誌,對應系統調用open的int flags參數。驅動程序爲了支持非阻塞型操作需要檢查這個標誌
*/
unsigned int f_flags;
/*
對文件的讀寫模式,對應系統調用open的mod_t mode參數。如果驅動程序需要這個值,可以直接讀取這個字段。
mod_t被定義爲:
typedef unsigned int __kernel_mode_t;
typedef __kernel_mode_t mode_t;
*/
fmode_t f_mode;
/*
當前的文件指針位置,即文件的讀寫位置
loff_t被定義爲:
typedef long long __kernel_loff_t;
typedef __kernel_loff_t loff_t;
*/
loff_t f_pos;
/*
struct fown_struct在linux/include/linux/fs.h被定義
struct fown_struct
{
rwlock_t lock; /* protects pid, uid, euid fields */
struct pid *pid; /* pid or -pgrp where SIGIO should be sent */
enum pid_type pid_type; /* Kind of process group SIGIO should be sent to */
uid_t uid, euid; /* uid/euid of process setting the owner */
int signum; /* posix.1b rt signal to be delivered on IO */
};
該結構的作用是通過信號進行I/O時間通知的數據
*/
struct fown_struct f_owner;
const struct cred *f_cred;
/*
struct file_ra_state結構被定義在/linux/include/linux/fs.h中
struct file_ra_state
{
pgoff_t start; /* where readahead started */
unsigned long size; /* # of readahead pages */
unsigned long async_size; /* do asynchronous readahead when
there are only # of pages ahead */
unsigned long ra_pages; /* Maximum readahead window */
unsigned long mmap_hit; /* Cache hit stat for mmap accesses */
unsigned long mmap_miss; /* Cache miss stat for mmap accesses */
unsigned long prev_index; /* Cache last read() position */
unsigned int prev_offset; /* Offset where last read() ended in a page */
};
該結構標識了文件預讀狀態,文件預讀算法使用的主要數據結構,當打開一個文件時,f_ra中出了perv_page(默認爲-1)和ra_apges(對該文件允許的最大預讀量)這兩個字段外,其他的所有西端都置爲0
*/
struct file_ra_state f_ra;
/*
記錄文件的版本號,每次使用後都自動遞增
*/
u64 f_version;
#ifdef CONFIG_SECURITY
/*
#ifdef CONFIG_SECURITY
void *f_security;
#endif
如果在編譯內核時配置了安全措施,那麼struct file結構中就會有void *f_security數據項,用來描述安全措施或者是記錄與安全有關的信息。
*/
void *f_security;
#endif
/*
系統在調用驅動程序的open方法前將這個指針置爲NULL。驅動程序可以將這個字段用於任意目的,也可以忽略這個字段。驅動程序可以用這個字段指向已分配的數據,但是一定要在內核釋放file結構前的release方法中清除它
*/
void *private_data;
#ifdef CONFIG_EPOLL
/*
被用在fs/eventpoll.c來鏈接所有鉤到這個文件上。其中
1) f_ep_links是文件的事件輪詢等待者鏈表的頭
2) f_ep_lock是保護f_ep_links鏈表的自旋鎖
*/
struct list_head f_ep_links;
struct list_head f_tfile_llink;
#endif /* #ifdef CONFIG_EPOLL */
/*
struct address_space被定義在/linux/include/linux/fs.h中,此處是指向文件地址空間的指針
*/
struct address_space *f_mapping;
#ifdef CONFIG_DEBUG_WRITECOUNT
unsigned long f_mnt_write_state;
#endif
};