Linux虛擬文件系統小結


1.inode

1).inode簡介

inode 是 UNIX/Linux 操作系統中的一種數據結構,其本質是結構體,它包含了與文件系統中各個文件相關的一些重要信息,例如文件及目錄的基本信息,包含時間、檔名、使用者及群組等。在 UNIX/Linux中創建文件系統時,同時將會創建大量的 inode 。通常,文件系統磁盤空間中大約百分之一空間分配給了 inode 表。在Linux系統中,內核為每一個新創建的文件分配一個Inode(索引結點),每個文件都有一個惟一的inode號,我們可以將inode簡單理解成一個指針,它永遠指向本文件的具體存儲位置。文件屬性保存在索引結點里,在訪問文件時,索引結點被復制到內存在,從而實現文件的快速訪問。系統是通過索引節點(而不是文件名)來定位每一個文件。

 

2).inode內容

Block 是記錄文件內容數據的區域,至於 inode 則是記錄"該文件的相關屬性,以及文件內容放置在哪一個 Block 之內"的信息,換句說, inode 除了記錄文件的屬性外,同時還必須要具有指向( pointer )的功能,亦即指向文件內容放置的區塊之中,好讓操作系統可以正確的去取得文件的內容,底下幾個是 inode 記錄的信息(當然不止這些):

  • 該文件的擁有者與群組(owner/group);
  • 該文件的存取模式(read/write/excute);
  • 該文件的類型(type);
  • 該文件建立或狀態改變的時間(ctime)、最近一次的讀取時間(atime)、最近修改的時間(mtime);
  • 該文件的容量;
  •  定義文件特性的旗標(flag),如 SetUID...;
  •  該文件真正內容的指向 (pointer);

3)inode結構體

inode結構體很大這里僅給出幾個重要成員

struct inode
{
    //........
    unsigned long           i_ino;            //節點號 
    atomic_t                i_count;          //引用計數
    //........
    uid_t                   i_uid;            //使用者id 
    gid_t                   i_gid;            //使用者id組 
    //........
    struct timespec         i_atime;          //最后訪問時間
    struct timespec         i_mtime;          //最后修改(modify)時間 
    struct timespec         i_ctime;          //最后改變(change)時間
    unsigned long           i_blocks;         //文件的塊數 
    unsigned short          i_bytes;          //使用的字節數
    unsigned long           i_state;          //狀態標志 
    struct list_head        i_devices;        //塊設備鏈表 
    unsigned char           i_sock;           //是否套接字 
    atomic_t                i_writecount;     //寫者計數
    //........
};

4).關於inode與dataarea

inode table是data area的索引表,data Area中存放真正的數據。

a. linux FS 可以簡單分成 inode table與data area兩部份。inode table上有許多的inode, 每個inode分別記錄一個檔案的屬性與這個檔案分布在哪些datablock上

b. inode table中紅色區域即inode size,是128Byte,在liunx系統上通過命令我們可以看到,系統就是這么定義的,Inode size是指分配給一個inode來記錄文檔屬性的磁盤塊的大小。

c. data ares中紫色的區域block size,就是我們一般概念上的磁盤塊。這塊區域是我們用來存放數據的地方。

d. 還有一個邏輯上的概念:FS中每分配2048 byte給data area, 就分配一個inode。但一個inode就並不一定就用掉2048 byte, 也不是說files allocation的最小單位是2048 byte, 它僅僅是代表filesystem中inode table/data area分配空間的比例是128/2048,也就是1/16。

e. inode參數是可以通過mkfs.ext3命令改變

 http://jixiuf.github.com/Linux/ext2.html#sec-2-1

注意:

i-node 是標記文件中數據快存儲位置的指針,一個I-NODE中能標記的數據快的個數是固定的,當文件很小時,一個文件對應一個I-node ,當文件比較大時一個文件對應多個I-NODE,一個inode對應一個文件,但文件可以有多個inode。是1:n的關系。

2.dentry

1)dentry簡介

dentry是Linux文件系統中某個索引節點(inode)的鏈接,每個dentry代表路徑中的一個特定部分。這個索引節點可以是文件,也可以是目錄。inode(可理解為ext2 inode)對應於物理磁盤上的具體對象dentry是一個內存實體,沒有對應的磁盤數據結構,VFS根據字符串形式的路徑名現場創建他,在dentry中,d_inode成員指向對應的inode。也就是說,一個inode可以在運行的時候鏈接多個dentry,而d_count記錄了這個鏈接的數量。另外dentry對象有三種狀態:被使用,未被使用和負狀態。

2).dentry的結構體

struct dentry { 
        atomic_t                   d_count;              //目錄項對象使用計數器,可以有未使用態,使用態和負狀態                                            
        unsigned int               d_flags;              //目錄項標志 
        struct inode               *d_inode;             //與文件名關聯的索引節點 
        struct dentry              *d_parent;            //父目錄的目錄項對象 
        struct list_head           d_hash;               //散列表表項的指針 
        struct list_head           d_lru;                //未使用鏈表的指針 
        struct list_head           d_child;              //父目錄中目錄項對象的鏈表的指針 
        struct list_head           d_subdirs;            //對目錄而言,表示子目錄目錄項對象的鏈表 
        struct list_head           d_alias;              //相關索引節點(別名)的鏈表 
        int                        d_mounted;            //對於安裝點而言,表示被安裝文件系統根項 
        struct qstr                d_name;               //文件名 
        unsigned long              d_time;               /* used by d_revalidate */ 
        struct dentry_operations   *d_op;                //目錄項方法 
        struct super_block         *d_sb;                //文件的超級塊對象 
        vunsigned long             d_vfs_flags; 
        void                       *d_fsdata;            //與文件系統相關的數據 
        unsigned char              d_iname [DNAME_INLINE_LEN]; //存放短文件名
};

 

下面給出進一步的解釋

      一個有效的dentry結構必定有一個inode結構,這是因為一個目錄項要么代表着一個文件,要么代表着一個目錄,而目錄實際上也是文件。所以,只要dentry結構是有效的,則其指針d_inode必定指向一個inode結構。可是,反過來則不然,一個inode卻可能對應着不止一個dentry結構;也就是說,一個文件可以有不止一個文件名或路徑名。這是因為一個已經建立的文件可以被連接(link)到其他文件名。所以在inode結構中有一個隊列i_dentry,凡是代表着同一個文件的所有目錄項都通過其dentry結構中的d_alias域掛入相應inode結構中的i_dentry隊列。

    在內核中有一個哈希表dentry_hashtable ,是一個list_head的指針數組。一旦在內存中建立起一個目錄節點的dentry 結構,該dentry結構就通過其d_hash域鏈入哈希表中的某個隊列中。

    內核中還有一個隊列dentry_unused,凡是已經沒有用戶(count域為0)使用的dentry結構就通過其d_lru域掛入這個隊列。

    Dentry結構中除了d_alias 、d_hash、d_lru三個隊列外,還有d_vfsmnt、d_child及d_subdir三個隊列。其中d_vfsmnt僅在該dentry為一個安裝點時才使用。另外,當該目錄節點有父目錄時,則其dentry結構就通過d_child掛入其父節點的d_subdirs隊列中,同時又通過指針d_parent指向其父目錄的dentry結構,而它自己各個子目錄的dentry結構則掛在其d_subdirs域指向的隊列中。

     從上面的敘述可以看出,一個文件系統中所有目錄項結構或組織為一個哈希表,或組織為一顆樹,或按照某種需要組織為一個鏈表,這將為文件訪問和文件路徑搜索奠定下良好的基礎。

 

3.file

1)文件對象和file簡介

文件對象表示進程已打開的文件,該對象file(不是物理文件)由相應的open()系統調用創建,有close()系統調用銷毀,因為多個進程可以同時打開和操作一個文件,所以同一個文件也可能存在多個對應的文件對象。文件對象僅僅在進程觀點上代表已打開文件,它反過來指向目錄項對象(反過來指向索引節點),其實只有目錄項對象才表示已打開的實際文件,雖然一個文件對應的文件對象不是是唯一的,但對應的索引節點和目錄項對象無疑是唯一的,另外類似於目錄項對象,文件對象實際上沒有對應的磁盤數據。

一個文件對象是由一個文件結構體表示的,文件結構體代表一個打開的文件,系統中的每個打開的文件在內核空間都有一個關聯的 struct file。它由內核在打開文件時創建,並傳遞給在文件上進行操作的任何函數。在文件的所有實例都關閉后,內核釋放這個數據結構。

 

2)file的結構體

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參數
        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;   f_ep_lock是保護f_ep_links鏈表的自旋鎖。
        #endif /* #ifdef CONFIG_EPOLL */
        struct address_space              *f_mapping; 文件地址空間的指針
};

 

 

4.files_struct

1).用戶打開文件表和files_struct簡介

每個進程用一個files_struct結構來記錄文件描述符的使用情況,這個files_struct結構稱為用戶打開文件表,它是進程的私有數據,

 

2)files_struct的結構體

struct files_struct {
     atomic_t               count;               /* 共享該表的進程數 */
     rwlock_t               file_lock;           /* 保護該結構體的鎖*/
     int                    max_fds;             /*當前文件對象的最大數*/
     int                    max_fdset;           /*當前文件描述符的最大數*/
     int                    next_fd;             /*已分配的文件描述符加1*/
     struct file            ** fd;               /* 指向文件對象指針數組的指針 */
     fd_set                 *close_on_exec;      /*指向執行exec()時需要關閉的文件描述符*/
     fd_set                 *open_fds;           /*指向打開文件描述符的指針*/
     fd_set                 close_on_exec_init;  /* 執行exec()時關閉的初始文件*/
     fd_set                 open_fds_init;       /*文件描述符的初值集合*/
     struct file            * fd_array[32];      /* 文件對象指針的初始化數組*/
};

下面給出一部分解釋

      fd域指向文件對象的指針數組。該數組的長度存放在max_fds域中。通常,fd域指向files_struct結構的fd_array域,該域包括32個文件對象指針。如果進程打開的文件數目多於32,內核就分配一個新的、更大的文件指針數組,並將其地址存放在fd域中;內核同時也更新max_fds域的值。

      對於在fd數組中有入口地址的每個文件來說,數組的索引就是文件描述符(file descriptor)。通常,數組的第一個元素(索引為0)是進程的標准輸入文件數組的第二個元素(索引為1)是進程的標准輸出文件,數組的第三個元素(索引為2)是進程的標准錯誤文件。請注意,借助於dup()、dup2()和 fcntl ) 系統調用,兩個文件描述符就可以指向同一個打開的文件,也就是說,數組的兩個元素可能指向同一個文件對象。當用戶使用shell結構(如2>&1)將標准錯誤文件重定向到標准輸出文件上時,用戶總能看到這一點。

      open_fds域包含open_fds_init域的地址,open_fds_init域表示當前已打開文件的文件描述符的位圖。max_fdset域存放位圖中的位數。由於數據結構fd_set有1024位,通常不需要擴大位圖的大小。不過,如果確實必須的話,內核仍能動態增加位圖的大小,這非常類似文件對象的數組的情形。

      當開始使用一個文件對象時調用內核提供的fget()函數。這個函數接收文件描述符fd作為參數,返回在current->files->fd[fd]中的地址,即對應文件對象的地址,如果沒有任何文件與fd對應,則返回NULL。在第一種情況下,fget()使文件對象引用計數器f_count的值增1。

-----------------------------

用戶打開文件表files_structs是由進程描述符task_struct中的file域指向,所有與進程相關的信息如打開的文件及文件描述符都包含其中,又從前面可以看出,files_struct通過**file保持對文件對象file的訪問,於此相似文件對象file的結構體內成員中包含目錄項dentry,目錄項dentry將文件名與inode相連,最終通過inode中的指針可以訪問存儲實際的數據的地方Data Area.

so他們管理層次關系關系如下:進程->task_struct->files_struct->file->dentry->inode->Data Area

如下圖

-----------------------------

 

5.硬鏈接與軟鏈接

1)硬鏈接

由於linux下的文件是通過索引節點(inode)來識別文件,硬鏈接可以認為是一個指針,指向原文件inode的指針,系統並不為它重新分配inode和創建文件;即硬鏈接文件和原文件其實是同一個文件,只是名字不同。每添加一個硬鏈接,文件inode的鏈接數就加1;刪除一個硬鏈接,inode的鏈接數減1,文件內容依然存在,直到inode的鏈接數為0,才刪除inode對應的文件。

特點:硬鏈接只能引用同一文件系統中的文件。它引用的是文件在文件系統中的物理索引(也稱為inode)。當移動或刪除原始文件時,硬鏈接不會被破壞,因為它所引用的是文件的物理數據而不是文件在文件結構中的位置。硬鏈接的文件不需要用戶有訪問原始文件的權限,也不會顯示原始文件的位置,這樣有助於文件的安全。如果刪除的文件有相應的硬鏈接,那么這個文件依然會保留,直到所有對它的引用都被刪除。

可以用ln命令來建立硬鏈接。語法:

ln [options] existingfile newfile 
ln [options] existingfile-list directory

用法:

  • 第一種:為 existingfile 創建硬鏈接,文件名為 newfile 。
  • 第二種:在 directory 目錄中,為 existingfile-list 中包含的所有文件創建一個同名的硬鏈接。

常用選項[options]

  • -f 無論 newfile 存在與否,都創建鏈接。
  • -n 如果 newfile 已存在,就不創建鏈接。

硬鏈接的不足:

  • 不可以在不同文件系統的文件間建立鏈接
  • 只有超級用戶才可以為目錄創建硬鏈接

 

2)軟鏈接

軟鏈接也叫符號鏈接,它是指向另一個文件的特殊文件,這種文件的數據部分僅包含它所要鏈接文件的路徑名。軟鏈接是為了克服硬鏈接的不足而引入的,鏈接不直接使用inode號作為文件指針,而是使用文件路徑名作為指針軟鏈接:文件名+ 數據部分–>目標文件的路徑名)。軟鏈接有自己的inode,並在磁盤上有一小片空間存放路徑名。因此,軟鏈接能夠跨文件系統,也可以和目錄鏈接!其二,軟鏈接可以對一個不存在的文件名進行鏈接,但直到這個名字對應的文件被創建后,才能打開其鏈接

軟鏈接克服了硬鏈接的不足,沒有任何文件系統的限制,任何用戶可以創建指向目錄的符號鏈接。因而現在更為廣泛使用,它具有更大的靈活性,甚至可以跨越不同機器、不同網絡對文件進行鏈接,如同Windows下的快捷方式。

可以用:ln -s 命令來建立軟鏈接:

ln -s existingfile newfile
ln -s existingfile-list directory


3).兩者的區別

軟鏈接與硬鏈接的區別不僅僅是在概念上,在實現上也是不同的,整理如下:

  • 對於硬鏈接,原文件和硬鏈接文件公用一個inode號,這說明他們是同一個文件,而對於軟鏈接,原文件和軟鏈接文件擁有不同的inode號,表明他們是兩個不同的文件
  • 在文件屬性上軟鏈接明確寫出了是鏈接文件,而硬鏈接沒有寫出來,因為在本質上硬鏈接文件和原文件是完全平等關系
  • 鏈接數目是不一樣的,軟鏈接的鏈接數目不會增加
  • 文件大小是不一樣的,硬鏈接文件顯示的大小是跟原文件是一樣的,因為是等同的,而這里軟鏈接顯示的大小與原文件就不同了,file1大小是48B,而file1soft是5B,這里面的5實際上就是“file1”的大小。
  • 在同一文件系統下,可以創建軟鏈接或硬鏈接(同文件系統不同目錄下也可以)。

總之,建立軟鏈接就是建立了一個新文件。當訪問鏈接文件時,系統就會發現他是個鏈接文件,它讀取鏈接文件找到真正要訪問的文件。

當然軟鏈接也有硬鏈接沒有的缺點,因為鏈接文件包含有原文件的路徑信息,所以當原文件從一個目錄下移到其他目錄中,再訪問鏈接文件,系統就找不到了,而硬鏈接就沒有這個缺陷,想怎么移就怎么移;還有它要系統分配額外的空間用於建立新的索引節點和保存原文件的路徑。

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM