Linux內核編程之文件系統-VFS中的目錄項對象和文件對象

一 .VFS 中的目錄項對象

1.爲了方便查找,VFS引入了 目錄 項,每個dentry代表路徑中的一個特定部分。目錄項也可包括安裝點。
2.目錄項對象由dentry結構體表示 ,定義在文件linux/dcache.h 頭文件中。
  89struct dentry {
  90        atomic_t d_count;                //使用計數
  91        unsigned int d_flags;           //目錄項標時
  92        spinlock_t d_lock;                //單目錄鎖
  93        int d_mounted;                    //目錄項的安裝點
  94        struct inode *d_inode;        //與該目錄項相關聯的索引節點
  95                                       
  96        /*
  97         * The next three fields are touched by __d_lookup.  Place them here
  98         * so they all fit in a cache line.
  99         */
 100        struct hlist_node d_hash;        //散列表
 101        struct dentry *d_parent;          //父目錄項
 102        struct qstr d_name;                //目錄項名可快速查找
 103
 104        struct list_head d_lru;            // 未使用目錄以LRU 算法鏈接的鏈表
 105        /*
 106         * d_child and d_rcu can share memory
 107         */
 108        union {
 109                struct list_head d_child;       /* child of parent list */
 110                struct rcu_head d_rcu;
 111        } d_u;
 112        struct list_head d_subdirs;     //該目錄項子目錄項所形成的鏈表
 113        struct list_head d_alias;        //索引節點別名鏈表
 114        unsigned long d_time;           //重新生效時間
 115        const struct dentry_operations *d_op;    // 操作目錄項的函數
 116        struct super_block *d_sb;       //目錄項樹的根
 117        void *d_fsdata;                        //具體文件系統的數據
 118
 119        unsigned char d_iname[DNAME_INLINE_LEN_MIN];    //短文件名
 120};
1>索引節點中的i_dentry指向了它目錄項,目錄項中的d_alias,d_inode又指會了索引節點對象,目錄項中的d_sb又指回了超級塊對象。
2>我們可以看到不同於VFS 中的索引節點對象和超級塊對象,目錄項對象中沒有對應磁盤的數據結構,所以說明目錄項對象並沒有真正標存在磁盤上,那麼它也就沒有髒標誌位。
3>目錄項的狀態(被使用,未被使用和負狀態)
a.它們是靠d_count的值來進行區分的,當d_count爲正值說明目錄項處於被使用狀態。當d_count=0時表示該目錄項是一個未被使用的目錄項, 但其d_inode指針仍然指向相關的的索引節點。該目錄項仍然包含有效的信息,只是當前沒有人引用他。d_count=NULL表示負(negative)狀態,與目錄項相關的inode對象不復存在(相應的磁盤索引節點可能已經被刪除),dentry對象的d_inode 指針爲NULL。但這種dentry對象仍然保存在dcache中,以便後續對同一文件名的查找能夠快速完成。這種dentry對象在回收內存時將首先被釋放。
4> d_subdirs:如果當前目錄項是一個目錄,那麼該目錄下所有的子目錄形成一個鏈表。該字段是這個鏈表的表頭;
      d_child:如果當前目錄項是一個目錄,那麼該目錄項通過這個字段加入到父目錄的d_subdirs鏈表當中。這個字段中的next和prev指針分別 指向父目錄中的另外兩個子目錄;
      d_alias:一個inode可能對應多個目錄項,所有的目錄項形成一個鏈表。inode結構中的i_dentry即爲這個鏈表的頭結點。當前目錄項以這個字段處於i_dentry鏈表中。www.linuxidc.com該字段中的prev和next指針分別指向與該目錄項同inode的其他兩個(如果有的話)目錄項
3.dentry和inode的區別:
 inode(可理解爲ext2 inode)對應於物理磁盤上的具體對象,dentry是一個內存實體,其中的d_inode成員指向對應的inode。也就是說,一個inode可以在運行的時候鏈接多個dentry,而d_count記錄了這個鏈接的數量。
4.dentry與dentry_cache
dentry_cache簡稱dcache,中文名稱是目錄項高速緩存,是Linux爲了提高目錄項對象的處理效率而設計的。它主要由兩個數據結構組成:
1>哈希鏈表dentry_hashtable:dcache中的所有dentry對象都通過d_hash指針域鏈到相應的dentry哈希鏈表中。
2>未使用的dentry對象鏈表dentry_unused:dcache中所有處於unused狀態和negative狀態的dentry對象都通過其d_lru指針域鏈入dentry_unused鏈表中。該鏈表也稱爲LRU鏈表。
目錄項高速緩存dcache是索引節點緩存icache的主控器(master),也即 dcache中的dentry對象控制着icache中的inode對象的生命期轉換。無論何時,只要一個目錄項對象存在於dcache中(非 negative狀態),則相應的inode就將總是存在,因爲 inode的引用計數i_count總是大於0。當dcache中的一個dentry被釋放時,針對相應inode對象的iput()方法就會被調用。
5對目錄項進行操作的一組函數叫目錄項操作表,由dentry_operation結構描述。它可以在 include/linux/dcache.h 中查到
 134struct dentry_operations {
 135        int (*d_revalidate)(struct dentry *, struct nameidata *);
 136        int (*d_hash) (struct dentry *, struct qstr *);
 137        int (*d_compare) (struct dentry *, struct qstr *, struct qstr *);
 138        int (*d_delete)(struct dentry *);
 139        void (*d_release)(struct dentry *);
 140        void (*d_iput)(struct dentry *, struct inode *);
 141        char *(*d_dname)(struct dentry *, char *, int);
 142};
a.int d_reavlidate(struct dentry *dentry ,int flags) 該函數判斷目錄對象是否有效。VFS準備從dcache中使用一個目錄項時,會調用該函數.
b.int d_hash(struct dentry *dentry ,struct qstr *name):該目錄生成散列值,當目錄項要加入到散列表時,VFS要調用此函數。
c.int d_compare( struct dentry *dentry, struct qstr *name1, struct qstr *name2) 該函數來比較name1和name2這兩個文件名。使用該函數要加dcache_lock鎖。
d.int d_delete(struct dentry *dentry):當d_count=0時,VFS調用次函數。使用該函數要叫 dcache_lock鎖。
e.void d_release(struct dentry *dentry):當該目錄對象將要被釋放時,VFS調用該函數。
f.void d_iput(struct dentry *dentry,struct inode *inode)當一個目錄項丟失了其索引節點時,VFS就掉用該函數。
二.VFS中的文件對象
1.文件對象表示進程已經打開的文件 在內存中的表示,該對象不是物理上的文件。它是由相應的open()系統調用創建,由close()系統調用銷燬。多個進程可以打開和操作同一個文件,所以同一個文件也可能存在多個對應的文件對象。
2一個文件對應的文件對象不是唯一的,但對應的索引節點和超級塊對象是唯一的。
3.file結構中保存了文件位置,此外,還把指向該文件索引節點的指針也放在其中。file結構形成一個雙鏈表,稱爲系統打開文件表 。它的定義在 include/linux/fs.h 中可以看到
 909struct file {
 910        /*
 911         * fu_list becomes invalid after file_free is called and queued via
 912         * fu_rcuhead for RCU freeing
 913         */
 914        union {
 915                struct list_head        fu_list;                            //每個文件系統中被打開的文件都會形成一個雙鏈表
 916                struct rcu_head       fu_rcuhead;
 917        } f_u;
 918        struct path                           f_path;                    
 919#define f_dentry                      f_path.dentry            // 與該文件對應的dentry
 920#define f_vfsmnt                     f_path.mnt
 921        const struct file_operations    *f_op;            //指向文件操作表的指針
 922        spinlock_t                        f_lock;  /* f_ep_links, f_flags, no IRQ */
 923#ifdef CONFIG_SMP
 924        int                                    f_sb_list_cpu;         
 925#endif
 926        atomic_long_t                 f_count;                     //文件對象的使用計數
 927        unsigned int                    f_flags;                      //打開文件時所指定的標誌
 928        fmode_t                          f_mode;                    //文件的訪問模式
 929        loff_t                               f_pos;                         //文件當前的位移量
 930        struct fown_struct           f_owner
 931        const struct cred            *f_cred;                    
 932        struct file_ra_state          f_ra;                          //預讀狀態
 933
 934        u64                                 f_version;                  //版本號
 935#ifdef CONFIG_SECURITY
 936        void                                *f_security;                //安全模塊
 937#endif
 938        /* needed for tty driver, and maybe others */
 939        void                                *private_data;           //tty設備hook
 940
 941#ifdef CONFIG_EPOLL
 942        /* Used by fs/eventpoll.c to link all the hooks to this file */
 943        struct list_head                f_ep_links;                
 944#endif /* #ifdef CONFIG_EPOLL */
 945        struct address_space    *f_mapping;                //頁緩存映射
 946#ifdef CONFIG_DEBUG_WRITECOUNT
 947        unsigned long f_mnt_write_state;
 948#endif
 949};
1>文件對象實際上沒有對應的磁盤數據,所以在結構體中沒有代表其對象是否爲髒,是否需要寫回磁盤的標誌。文件對象 通過f_path.dentry指針指向相關的目錄項對象。目錄項會指向相關的索引節點,索引節點會記錄文件是否是髒的。
2>fu_list:每個文件系統中以被打開的文件都會形成一個雙聯表,這個雙聯表的頭結點存放在超級塊的s_files字段中。該字段的prev和next指針分別指向在鏈表中與當前文件結構體相鄰的前後兩個元素.


Tiger-John說明:

file結構中主要保存了文件位置,此外還把指向該文件索引節點的指針也放在其中。---》有人就問了,問什麼不直接把文件位置存放在索引節點中呢?

因爲:Linux中的文件是能夠共享的,假如把文件位置存放在索引節點中,當有兩個或更多個進程同時打開一個文件時,它們將去訪問同一個索引節點,那麼一個進程的lseek操作將影響到另一個進程的讀操作,這顯然是致命的錯誤。

 


4>對文件進行操作的一組函數叫文件操作表,由file_operations結構定義:可以在include/linux/fs.h 中查看
1488struct file_operations {
1489        struct module *owner;
1490        loff_t (*llseek) (struct file *, loff_t, int);
1491        ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
1492        ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
1493        ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
1494        ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
1495        int (*readdir) (struct file *, void *, filldir_t);
1496        unsigned int (*poll) (struct file *, struct poll_table_struct *);
1497        long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
1498        long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
1499        int (*mmap) (struct file *, struct vm_area_struct *);
1500        int (*open) (struct inode *, struct file *);
1501        int (*flush) (struct file *, fl_owner_t id);
1502        int (*release) (struct inode *, struct file *);
1503        int (*fsync) (struct file *, int datasync);
1504        int (*aio_fsync) (struct kiocb *, int datasync);
1505        int (*fasync) (int, struct file *, int);
1506        int (*lock) (struct file *, int, struct file_lock *);
1507        ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
1508        unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
1509        int (*check_flags)(int);
1510        int (*flock) (struct file *, int, struct file_lock *);
1511        ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
1512        ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
1513        int (*setlease)(struct file *, long, struct file_lock **);
1514};
owner:用於指定擁有這個文件操作結構體的模塊,通常取THIS_MODULE;
llseek:用於設置文件的偏移量。第一個參數指明要操作的文件,第二個參數爲偏移量,第三個參數爲開始偏移的位置(可取SEEK_SET,SEEK_CUR和SEEK_END之一)。
read:從文件中讀數據。第一個參數爲源文件,第二個參數爲目的字符串,第三個參數指明欲讀數據的總字節數,第四個參數指明從源文件的某個偏移量處開始讀數據。由系統調用read()調用;
write:往文件裏寫數據。第一個參數爲目的文件,第二個參數源字符串,第三個參數指明欲寫數據的總字節數,第四個參數指明從目的文件的某個偏移量出開始寫數據。由系統調用write()調用;
mmap:將指定文件映射到指定的地址空間上。由系統調用mmap()調用;
open:打開指定文件,並且將這個文件和指定的索引結點關聯起來。由系統調用open()調用;
release:釋放以打開的文件,當打開文件的引用計數(f_count)爲0時,該函數被調用;
fsync():文件在緩衝的數據寫回磁盤
3.用戶打開文件表
系統中的每一個進程都有自己的一組打開的文件 ,像根文件系統,當前目工作目錄,安裝點等。有四個數據結構將VFS層和系統的進程緊密聯繫在一起,它們分別是:files_struct,fs_struct, file_system_type 和namespace結構體。

我們先看兩個圖:

 

 

此圖來自陳莉君老師 博客,版權歸屬於陳莉君老師。        
1.>文 件描述符是用來描述打開的文件的。每個進程用一個files_struct結構來記錄文件描述符的使用情況,這個結構稱爲用戶打開文件表。它是進程的私有數據。
a.files_struct 結構體定義在文件 include/linux/fdtable.h該結構體由進程描述符中的files 域指向。所有與每個進程(per-process)相關的信息如打開的文件及文件描述符都包含在其中。
  44struct files_struct {
  45  /*
  46   * read mostly part
  47   */
  48        atomic_t count;                           //共享該表的進程數
  49        struct fdtable *fdt;                    
  50        struct fdtable fdtab;                  //定義了文件的一些屬性
  51  /*
  52   * written part on a separate cache line in SMP
  53   */
  54        spinlock_t file_lock ____cacheline_aligned_in_smp;
  55        int next_fd;                                  //下一個文件描述符
  56        struct embedded_fd_set close_on_exec_init;   //*exec()關閉的文件描述符
  57        struct embedded_fd_set open_fds_init;        //文件描述符的初始集合
  58        struct file * fd_array[NR_OPEN_DEFAULT];    //默認的文件對象數組
  59};
a.看看 struct fdtable結構:
32struct fdtable {
  33        unsigned int max_fds;   //文件對象的上限
  34        struct file ** fd;      //全部文件對象數組
  35        fd_set *close_on_exec;   //*exec()關閉的文件描述符
  36        fd_set *open_fds;        //指向打開文件的描述符
  37        struct rcu_head rcu;   
  38        struct fdtable *next;    //指向該鏈表的下一個對象
  39};
  40
b.fd數組指針指向已打開的文件對象鏈表,默認情況下,指向fd_arry數組。因爲NR_OPEN_DEFAULT等於32,所以該數組可以容納32個文件對象。如果一個進程所打開的文件對象超過32個。內核將分配一個新數組,並且將fd指向它。

 
c.對於在fd數組中有入口地址的每個文件來說,數組的索引就是文件描述符。通常,數組的第一個元素(索引爲0)表示進程的標準輸入文件,數組的第二個元素(索引爲1)是進程的標準輸出文件,數組的第三個元素(索引爲2)是進程的標準錯誤文件)
2.>fs_struct 結構
a.fs_struct 結構描述進程與文件系統的關係
b.我們來深入分析其代碼,它的定義在 include/linux/fs_struct.h,
  6struct fs_struct {
   7        int users;           
   8        spinlock_t lock;                  //保護該結構體的鎖
   9        int umask;                          //默認的文件訪問權限
  10        int in_exec;
  11        struct path root, pwd;   
  12};
看一下struct path 結構的定義 :
  7struct path {
   8        struct vfsmount *mnt;
   9        struct dentry *dentry;
  10}
可以看到struct path 封裝了vfsmount 和dentry;,所以struct path root,pwd包含了當前進程的當前工作目錄和根目錄以及根目錄安裝點對象和pwd安裝點對象。

3>file_system_type結構體

在Linux中,用file_system_type來描述各種特定文件系統類型,比如ext3。也就是說Linux支持的所有文件系統類型都分別唯一的對應一個file_system_type結構


a.它的定義在 include/linux/fs.h中。

1736struct file_system_type {
1737        const char *name;                                                  //文件系統的類型名
1738        int fs_flags;                                                              //文件系統類型標誌
1739        int (*get_sb) (struct file_system_type *, int,         //文件系統讀入其超級塊的函數指針
1740                       const char *, void *, struct vfsmount *);
1741        void (*kill_sb) (struct super_block *);                 //該函數用來終止訪問超級塊
1742        struct module *owner;       //通常設置爲宏THIS_MODLUE,用以確定是否把文件系統作爲模塊安裝
1743        struct file_system_type * next; 
1744        struct list_head fs_supers;
1745
1746        struct lock_class_key s_lock_key;
1747        struct lock_class_key s_umount_key;
1748        struct lock_class_key s_vfs_rename_key;
1749
1750        struct lock_class_key i_lock_key;
1751        struct lock_class_key i_mutex_key;
1752        struct lock_class_key i_mutex_dir_key;
1753        struct lock_class_key i_alloc_sem_key;
1754};


tiger-john說明:

1>name:文件系統的名字,不能爲空;
2>get_sb:在安裝文件系統時,調用此指針所指函數以在磁盤中獲取超級塊;
3>kill_sb:卸載文件文件系統時候,調用此指針所指函數以進行一些清理工作;
4>owner:如果一個文件系統以模塊的形式加載到內核,則該字段用來說明哪個模塊擁有這個結構。一般爲THIS_MODULE;
5>next:所有的文件系統類型結構形成一個鏈表,該鏈表的頭指針爲全局變量file_systems(struct file_system_type *file_systems)。這個字段指向鏈表中下一個文件系統類型結構;
6>fs_supers:同一個文件系統類型下的所有超級塊形成一個雙聯表,這個字段是這個雙聯表的頭結點。超級塊之間通過s_instances字段相互鏈接.

4>vfsmount結構體

a.當文件系統被實際安裝時,將有一個vfsmount 結構體在安裝點被創建。該結構體用來代表文件系統的實例即代表一個安裝點。

b.vfsmount結構體被定義在 include/linux/mount.h中

 36/*
  37 * MNT_SHARED_MASK is the set of flags that should be cleared when a
  38 * mount becomes shared.  Currently, this is only the flag that says a


  39 * mount cannot be bind mounted, since this is how we create a mount
  40 * that shares events with another mount.  If you add a new MNT_*
  41 * flag, consider how it interacts with shared mounts.
  42 */
  43#define MNT_SHARED_MASK (MNT_UNBINDABLE)
  44#define MNT_PROPAGATION_MASK    (MNT_SHARED | MNT_UNBINDABLE)
  45
  46
  47#define MNT_INTERNAL    0x4000
  48
  49struct vfsmount {
  50        struct list_head mnt_hash;             //散列表
  51        struct vfsmount *mnt_parent;         //指向上一層安轉點的指針
  52        struct dentry *mnt_mountpoint;      //安裝點的目錄項
  53        struct dentry *mnt_root;                   //安裝樹的根
  54        struct super_block *mnt_sb;          //指向超級塊的指針
  55        struct list_head mnt_mounts;       //子鏈表
  56        struct list_head mnt_child;          / /通過mnt_child進行遍歷
  57        int mnt_flags;                                 //安裝標誌
  58        /* 4 bytes hole on 64bits arches without fsnotify */
  59#ifdef CONFIG_FSNOTIFY
  60        __u32 mnt_fsnotify_mask;
  61        struct hlist_head mnt_fsnotify_marks;
  62#endif
  63        const char *mnt_devname;        /* Name of device e.g. /dev/dsk/hda1 */
  64        struct list_head mnt_list;
  65        struct list_head mnt_expire ;    /* link in fs-specific expiry list */
  66        struct list_head mnt_share;      /* circular list of shared mounts */
  67        struct list_head mnt_slave_list;/* list of slave mounts */
  68        struct list_head mnt_slave;      /* slave list entry */
  69        struct vfsmount *mnt_master;    /* slave is on master->mnt_slave_list */
  70        struct mnt_namespace *mnt_ns;   /* containing namespace */
  71        int mnt_id;                     /* mount identifier */
  72        int mnt_group_id;               /* peer group identifier */
  73        /*
  74         * We put mnt_count & mnt_expiry_mark at the end of struct vfsmount
  75         * to let these frequently modified fields in a separate cache line
  76         * (so that reads of mnt_flags wont ping-pong on SMP machines)
  77         */
  78        atomic_t mnt_count;               //使用計數
  79        int mnt_expiry_mark;            /* true if marked for expiry */
  80        int mnt_pinned;
  81        int mnt_ghosts;
  82#ifdef CONFIG_SMP
  83        int __percpu *mnt_writers;
  84#else
  85        int mnt_writers;
  86#endif
  87};

a.vfsmount 結構還保存了在安裝時指定的標誌信息,該信息存儲在mmt_flags中。

MNT_NOSUID:禁止該文件系統的可執行文件設置setuid和setgid標誌

MNT_NODEV:禁止訪問該文件系統上的設備文件

MNT_NOEXEC:禁止執行該文件系統上的可執行文件

Tiger-John說明:

在管理員安裝一些不是很安全的移動設備時,這些標誌很有用。

b.爲了對系統中的所有安裝點進行快速查找,內核把它們按哈希表來組織,mnt_hash就是形成哈希表的隊列指針。
c.mnt_mountpoint是指向安裝點dentry結構的指針。而dentry指針指向安裝點所在目錄樹中根目錄的dentry結構。
d.mnt_parent是指向上一層安裝點的指針。如果當前的安裝點沒有上一層安裝點(如根設備),則這個指針爲NULL。同時,vfsmount結構中還有mnt_mounts和mnt_child兩個隊列頭,只要上一層vfsmount結構存在,就把當前vfsmount結構中mnt_child鏈入上一層vfsmount結構的mnt_mounts隊列中。這樣就形成一顆設備安裝的樹結構,從一個vfsmount結構的mnt_mounts隊列開始,可以找到所有直接或間接安裝在這個安裝點上的其他設備。如圖8.2。
e.mnt_sb指向所安裝設備的超級塊結構super_block。
f.mnt_list是指向vfsmount結構所形成鏈表的頭指針。

我們來看個圖:

 

本篇文章來源於 Linux公社網站(www.linuxidc.com)  原文鏈接:http://www.linuxidc.com/Linux/2011-02/32127p2.htm

發佈了5 篇原創文章 · 獲贊 12 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章