1. 程式人生 > >linux核心設計與實現——虛擬檔案系統

linux核心設計與實現——虛擬檔案系統

虛擬檔案系統

虛擬檔案系統(有時也稱作虛擬檔案交換,更常見的是簡稱VFS)作為核心子系統,為使用者空間程式提供了檔案和檔案系統相關的介面。

之所以可以使用這種通用介面對所有型別的檔案系統進行操作,是因為核心在它的底層檔案系統介面上建立了一個VFS抽象層,該抽象層使Linux能夠支援各種檔案系統,即便是它們在功能和行為上存在很大的差別。

VFS抽象層能夠支援各種各樣的檔案系統,因為它定義了所有檔案系統都支援的、基本的、概念上的介面和資料結構。而實際的檔案系統的程式碼在統一的介面和資料結構下隱藏了具體的實現細節。

Unix檔案系統

Unix使用了四種和檔案系統相關的傳統抽象概念:檔案、目錄項、索引節點和安裝點。
1. 檔案

:檔案就是一個有序位元組串,位元組串中第一個位元組是檔案的頭,最後一個位元組是檔案的尾。典型的檔案操作有讀、寫、建立和刪除等。
2. 目錄項:檔案路徑中的每一部分都被稱作目錄條目。“/home/wolfman/butter”是檔案路徑的一個例子——根目錄/,目錄home,wolfman和檔案butter都是目錄條目,它們統稱為目錄項。(在Unix中,目錄屬於普通檔案,它列出包含在其中的所有檔案。)
3. 索引節點:Unix系統將檔案的相關資訊和檔案本身這兩個概念加以區分,例如訪問控制權限、大小、擁有者、建立時間等資訊。檔案相關資訊,有時被稱作檔案的元資料,被儲存在一個單獨的資料結構中,該結構被稱為索引節點(inode)。
4. 安裝點
:在Unix中,檔案系統被安裝在一個特定的安裝點上,該安裝點在全域性層次結構中被稱作名稱空間,所有的已安裝檔案系統都作為根檔案系統樹的枝葉出現在系統中。

VFS物件及其資料結構

VFS採用的是面向物件的設計思路,但在C語言中,只能使用結構體方式實現,即包含資料的同時又包含操作這些資料的函式指標。

VFS中有四個主要的物件型別,它們分別是:

  • 超級塊物件,代表一個具體的已安裝檔案系統。
  • 索引節點物件,代表一個具體檔案。
  • 目錄項物件,代表一個目錄項,是路徑的一個組成部分。
  • 檔案物件,代表有程序開啟的檔案。

每個主要物件中都包含一個操作物件,這些操作物件描述了核心針對主要物件可以使用的方法:

  • super_operations物件:包含核心針對特定檔案系統所能呼叫的方法。
  • inode_operations物件:包含核心對特定檔案所能呼叫的方法。
  • dentry_operations物件:包含核心對特定目錄所能呼叫的方法。
  • file_operations物件:包含程序針對已開啟檔案所能呼叫的方法。

超級塊物件

各種檔案系統都必須實現超級塊物件,該物件用於儲存特定檔案系統的資訊,通常對應於存放在磁碟特定扇區的檔案系統超級塊或檔案系統控制塊。對於並非基於磁碟的檔案系統(基於記憶體的檔案系統sysfs、proc等),它們會在使用時建立超級塊並將其儲存到記憶體中。
超級塊物件由結構體super_block表示,定義在 linux/fs.h 中,下面列出了一些重要的屬性:

/**
 * 超級塊結構中定義的欄位非常多,
 * 這裡只介紹一些重要的屬性
 */
struct super_block {
    struct list_head                s_list;     /* 指向所有超級塊的連結串列 */
    const struct super_operations   *s_op;      /* 超級塊方法 */
    struct dentry                   *s_root;    /* 目錄掛載點 */
    struct mutex                    s_lock;     /* 超級塊訊號量 */
    int                             s_count;    /* 超級塊引用計數 */
    struct list_head                s_inodes;   /* inode連結串列 */
    struct mtd_info                 *s_mtd;     /* 儲存磁碟資訊 */
    fmode_t                         s_mode;     /* 安裝許可權 */
};

其中,s_op指向超級塊的操作函式表,由struct super_operations結構體表示,定義在 linux/fs.h 中,其形式如下:

/*
 * 其中的 s_op 中定義了超級塊的操作方法
 * 這裡只介紹一些相對重要的函式
 */
struct super_operations {

    /* 建立和初始化一個索引節點物件 */
    struct inode *(*alloc_inode)(struct super_block *sb);

    /* 釋放給定的索引節點 */
    void (*destroy_inode)(struct inode *);

    /* VFS在索引節點被修改時會呼叫這個函式 */
    void (*dirty_inode) (struct inode *); 

    /* 將索引節點寫入磁碟,wait表示寫操作是否需要同步 */
    int (*write_inode) (struct inode *, int);

    /* 最後一個指向索引節點的引用被刪除後,VFS會呼叫這個函式 */
    void (*drop_inode) (struct inode *);

    /* 從磁碟上刪除指定的索引節點 */
    void (*delete_inode) (struct inode *);

    /* 解除安裝檔案系統時由VFS呼叫,用來釋放超級塊 */
    void (*put_super) (struct super_block *);

    /* 用給定的超級塊更新磁碟上的超級塊 */
    void (*write_super) (struct super_block *);

    /* 使檔案系統中的資料與磁碟上的資料同步 */
    int (*sync_fs)(struct super_block *sb, int wait);

    /* VFS呼叫該函式獲取檔案系統狀態 */
    int (*statfs) (struct dentry *, struct kstatfs *);

    /* 指定新的安裝選項重新安裝檔案系統時,VFS會呼叫該函式 */
    int (*remount_fs) (struct super_block *, int *, char *);

    /* VFS呼叫該函式釋放索引節點,並清空包含相關資料的所有頁面 */
    void (*clear_inode) (struct inode *);

    /* VFS呼叫該函式中斷安裝操作 */
    void (*umount_begin) (struct super_block *);
};

索引節點物件

索引節點物件包含了核心在操作檔案或目錄時需要的全部資訊。索引節點物件在記憶體中建立,以便於檔案系統使用。

索引節點物件由inode結構體表示,它定義在 linux/fs.h 中,下面列出了一些重要的屬性:

/* 
 * 索引節點結構中定義的欄位非常多,
 * 這裡只介紹一些重要的屬性
 */
struct inode {
    struct hlist_node       i_hash;     /* 散列表,用於快速查詢inode */
    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;    /* 硬連結數 */
    uid_t                   i_uid;      /* 使用者id */
    gid_t                   i_gid;      /* 使用組id */
    struct timespec         i_atime;    /* 最後訪問時間 */
    struct timespec         i_mtime;    /* 最後修改時間 */
    struct timespec         i_ctime;    /* 最後改變時間 */
    const struct inode_operations       *i_op;      /* 索引節點操作函式 */
    const struct file_operations        *i_fop;     /* 預設的索引節點操作 */
    struct super_block      *i_sb;          /* 相關的超級塊 */
    struct address_space    *i_mapping;     /* 相關的地址對映 */
    struct address_space    i_data;         /* 裝置地址對映 */
    unsigned int            i_flags;        /* 檔案系統標誌 */
    void                    *i_private;     /* fs 私有指標 */
};

索引節點物件的操作方法由結構體inode_operations定義,這裡只介紹一些重要的函式:

struct inode_operations {

    /* 為dentry物件創造一個新的索引節點 */
    int (*create) (struct inode *,struct dentry *,int, struct nameidata *);

    /* 在特定資料夾中尋找索引節點,該索引節點要對應於dentry中給出的檔名 */
    struct dentry * (*lookup) (struct inode *,struct dentry *, struct nameidata *);

    /* 建立硬連結 */
    int (*link) (struct dentry *,struct inode *,struct dentry *);

    /* 從一個符號連結查詢它指向的索引節點 */
    void * (*follow_link) (struct dentry *, struct nameidata *);

    /* 在 follow_link呼叫之後,該函式由VFS呼叫進行清除工作 */
    void (*put_link) (struct dentry *, struct nameidata *, void *);

    /* 該函式由VFS呼叫,用於修改檔案的大小 */
    void (*truncate) (struct inode *);
};

目錄項物件

VFS把目錄當作檔案對待,檔案都可以由索引節點物件表示。
VFS經常需要執行目錄相關的操作,比如檔案查詢等。為了方便查詢操作,VFS引入了目錄項概念。在路徑中(包括普通檔案在內),每一個部分都是目錄項物件。
VFS在執行目錄操作時(如果需要的話)會現場建立目錄項物件。
目錄項物件由dentry結構體表示,定義在檔案 linux/dcache.h 中,下面列出幾個重要的選項:

struct dentry {
    atomic_t                d_count;        /* 使用計數 */
    unsigned int            d_flags;        /* 目錄項標識 */
    struct inode            *d_inode;       /* 相關聯的索引節點 */
    struct hlist_node       d_hash;         /* 散列表 */
    struct dentry           *d_parent;      /* 父目錄的目錄項物件 */
    struct qstr             d_name;         /* 目錄項名稱 */
    struct list_head        d_subdirs;      /* 子目錄連結串列 */
    struct list_head        d_alias;        /* 索引節點別名連結串列 */
    struct dentry_operations    *d_op;      /* 目錄項操作指標 */
    ...
}

目錄項物件有三種有效狀態:被使用、未被使用和負狀態。被使用和未被使用的目錄項都對應一個有效的索引節點,而負狀態的目錄項沒有對應的有效索引節點。

為了減少VFS層遍歷檔案路徑的時間,核心將目錄項物件快取在目錄項快取(簡稱dcache)中。目錄項快取包括三個主要部分:

  1. “被使用的”目錄項鍊表;
  2. “未被使用的”雙向連結串列;
  3. 散列表和相應的雜湊函式。

dentry_operations結構體指明瞭VFS操作目錄項的所有方法,定義在檔案 linux/dcache.h 中:

struct dentry_operations {

    /* 判斷目錄物件是否有效 */
    int (*d_revalidate)(struct dentry *, struct nameidata *);

    /* 為目錄項物件生成雜湊值 */
    int (*d_hash)(struct dentry *, struct qstr *);

    /* 比較name1和name2這兩個檔名 */
    int (*d_compare)(struct dentry *, struct qstr *, struct qstr *);

    /* 當目錄項物件的d_count計數值等於0時,VFS呼叫該函式. */
    int (*d_delete)(struct dentry *);

    /* 當目錄項物件將要被釋放時,VFS呼叫該函式。 */
    void (*d_release)(struct dentry *);

    /* 當一個目錄項物件丟失了其相關的索引節點時,VFS呼叫該函式 */
    void (*d_iput)(struct dentry *, struct inode *);

    char (*d_dname)(struct dentry *, char *, int);
}

檔案物件

檔案物件是已開啟的檔案在記憶體中的表示。該物件由相應的open()系統呼叫建立,由close()系統呼叫撤銷,所有這些檔案相關的呼叫實際上都是檔案操作表中定義的方法。
檔案物件由file結構體表示,定義在檔案 linux/fs.h 中,下面給出幾個重要的選項:

struct file {
    struct path f_path; /* 包含目錄項 */
    struct file_operations *f_op;  /* 檔案操作表 */
    atomic_t f_count; /* 檔案物件的使用計數 */
    unsigned int f_flags; /* 當開啟檔案時所指定的標誌 */
    mode_t f_mode; /* 檔案的訪問模式 */
    loff_t f_pos; /* 檔案當前的位移量(檔案指標)*/
    ......
}

檔案物件的操作由file_operations結構體表示,定義在檔案 linux/fs.h 中:

struct file_operations { 

    /* 擁有該結構的模組的指標,一般為THIS_MODULES */
    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);

    /* 僅用於讀取目錄,對於裝置檔案,該欄位為NULL */
    int (*readdir) (struct file *, void *, filldir_t);

    /* 輪詢函式,判斷目前是否可以進行非阻塞的讀寫或寫入 */
    unsigned int (*poll) (struct file *, struct poll_table_struct *); 

    /* 執行裝置I/O控制命令 */
    int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); 

    /* 不使用BLK檔案系統,將使用此種函式指標代替ioctl */
    long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);

    /* 在64位系統上,32位的ioctl呼叫將使用此函式指標代替 */
    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); 

    /* 通知裝置FASYNC標誌發生變化 */
    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 **); 

};

相關推薦

linux核心設計實現——虛擬檔案系統

虛擬檔案系統 虛擬檔案系統(有時也稱作虛擬檔案交換,更常見的是簡稱VFS)作為核心子系統,為使用者空間程式提供了檔案和檔案系統相關的介面。 之所以可以使用這種通用介面對所有型別的檔案系統進行操作,是因為核心在它的底層檔案系統介面上建立了一個VFS抽象層,該抽

Linux核心設計實現》讀書筆記(十三)- 虛擬檔案系統

虛擬檔案系統(VFS)是linux核心和具體I/O裝置之間的封裝的一層共通訪問介面,通過這層介面,linux核心可以以同一的方式訪問各種I/O裝置。 虛擬檔案系統本身是linux核心的一部分,是純軟體的東西,並不需要任何硬體的支援。 主要內容: 虛擬檔案系統的作用 虛擬檔案系統的4個主要物件

LINUX核心設計實現虛擬檔案系統

VFS作為核心子系統,為使用者空間程式提供了檔案系統的操作介面.VFS是使用者空間到具體檔案系統(如EXT3)的一個介面中間層. 12.1 通用檔案系統介面 VFS最大的意義就是使用使用者空間可以直接使用open()、read()和write()等等函式而不需要考慮具體的

Linux核心設計實現 總結筆記(第五章)系統呼叫

系統呼叫 核心提供了使用者程序和核心互動的介面,使得應用程式可以受限制的訪問硬體裝置。 提供這些介面主要是為了保證系統穩定可靠,避免應用程式恣意妄行。   一、核心通訊 系統呼叫在使用者空間程序和硬體裝置之間新增中間才能。作用有三: 為使用者空間提供一種硬體的抽象介面。無需理會物理

例項:基於4412-實現新增自己的系統呼叫函式(學習《Linux核心設計實現》 記錄)

學習筆記: 在學習《linux核心設計與實現》過程中,瞭解到: 在Linux中,系統呼叫是使用者空間訪問核心的唯一手段(除異常和陷入之外)。 系統呼叫主要有三個作用: ①:為使用者空間提供一個硬體的抽象介面。 ②:系統呼叫保證了系統的穩定和安全。 ③:為了實現多工和虛擬記憶體(應用程

6.Linux核心設計實現 P57---系統呼叫(轉)

在Linux中,系統呼叫是使用者空間訪問核心的唯一手段,它們是核心唯一的合法入口。實際上,其他的像裝置檔案和/proc之類的方式,最終也還是要通過系統呼叫進行的。       一般情況下,應用程式通過應用程式設計介面(API)而不是直接通過系統呼叫來程式設計,而且這種程式設計介面實際上並不需要和核心提供的系統

【讀書筆記】《Linux核心設計實現》程序管理程序排程

大學跟老師做嵌入式專案,寫過I2C的裝置驅動,但對Linux核心的瞭解也僅限於此。Android系統許多導致root的漏洞都是核心中的,研究起來很有趣,但看相關的分析文章總感覺隔著一層窗戶紙,不能完全理會。所以打算系統的學習一下Linux核心。買了兩本書《Linux核心設計與實現(第3版)》和《深入理解Lin

Linux核心設計實現(1)--核心開發的特點

1. 核心程式設計時既不能訪問C庫也不能訪問標準的C標頭檔案        其中的原因有很多種。其一,C標準庫的很多函式實現都是基於核心實現的,這核心編譯的時候都還沒有核心,所以就不存在這些函式,這個就是先有雞還是先有蛋這個悖論。其二,其主主要的的

Linux核心設計實現 總結筆記(第二章)

一、Linux核心中的一些基本概念 核心空間:核心可獨立於普通應用程式,它一般處於系統態,擁有受保護的記憶體空間和訪問硬體裝置的所有許可權。這種系統態和被保護起來的記憶體空間,稱為核心空間。 程序上下文:當應用程式執行一條系統呼叫,通過系統呼叫執行在核心空間,而核心被稱為執行在程序上下文中。  

Linux核心設計實現 總結筆記(第六章)核心資料結構

核心資料結構 Linux核心實現了這些通用資料結構,而且提倡大家在開發時重用。 核心開發者應該儘可能地使用這些資料結構,而不要自作主張的山寨方法。 通用的資料結構有以下幾種:連結串列、佇列、對映和二叉樹   一、連結串列 1.1 單向連結串列和雙向連結串列   1.2 環形

Linux核心設計實現》 讀書筆記

linux核心簡介 Linux系統的基礎是核心,C庫(庫函式裡會有些系統呼叫),工具集和系統的基本工具。 通常一個核心由負責響應中斷的中斷服務程式,負責管理多個程序從而分享處理器時間的排程程式,負責管理程序地址空間的記憶體管理程式,和網路、程序間通訊等系統服務程

Linux核心設計實現 原書第3版中文版pdf

                下載地址:網盤下載內容簡介編輯《Linux核心設計與實現(原書第3版)》基於Linux 2.6.34核心詳細介紹了Linux核心系統,覆蓋了從核心核心系統的應用到核心設計與實現等各方面的內容。《Linux核心設計與實現(原書第3版)》主要內容包括:程序管理、程序排程、時間管理和

例項:tasklet實現軟中斷(學習《Linux核心設計實現》記錄)

tasklet是通過軟中斷實現的,tasklet本身也是軟中斷。 關於tasklet更詳細的知識,還是建議看一下《Linux核心設計與實現》 本貼子只介紹一下具體的流程。 驅動程式原始碼: #include <linux/init.h> #include <linu

4.Linux核心設計實現 P31---淺析程序終結關鍵do_exit(轉)

程序在退出時,必須釋放它所擁有的資源,並通過某種方式告訴父程序。程序的退出一般是顯示或隱式地呼叫了eixt(),或者接受了某種訊號。不過什麼原因退出,最終都呼叫了do_exit。用於程序退出的系統呼叫有兩個exit和exit_group,exit只是終止某個程序,而exit_group整個執行緒中的程序。它們

7.Linux核心設計實現 P69---深入分析 Linux 核心連結串列(轉)

連結串列是一種常用的組織有序資料的資料結構,它通過指標將一系列資料節點連線成一條資料鏈,是線性表的一種重要實現方式。相對於陣列,連結串列具有更好的動態性,建立連結串列時無需預先知道資料總量,可以隨機分配空間,可以高效地在連結串列中的任意位置實時插入或刪除資料。連結串列的開銷主要是訪問的順序性和組織鏈的空間

5.Linux核心設計實現 P39---linux2.6 CFS排程演算法分析(轉)

1.概述      CFS(completely fair schedule)是最終被核心採納的排程器。它從RSDL/SD中吸取了完全公平的思想,不再跟蹤程序的睡眠時間,也不再企圖區分互動式程序。它將所有的程序都統一對待,這就是公平的含義。CFS的演算法和實現都相當簡單,眾多的測試表明其效能也非常優越。   

11.Linux核心設計實現 P160---順序鎖總結 (轉)

當使用讀/寫自旋鎖時,核心控制路徑發出的執行read_lock或write_lock操作的請求具有相同的優先權:讀者必須等待,直到寫操作完成。同樣地,寫者也必須等待,直到讀操作完成。Linux 2.6中引入了順序鎖(seqlock),它與讀/寫自旋鎖非常相似,只是它為寫者賦予了較高的優先順序:事實上,即使在讀

8.Linux核心設計實現 P77---list_for_each()list_for_each_safe()的區別 (轉)

list_for_each()的定義: /**   * list_for_each    -   iterate over a list   * @pos:    the &struct list_head to use as a loop counter.   * @head:   the hea

9.Linux核心設計實現 P91---中斷和中斷處理程式 (轉)

      中斷還是中斷,我講了很多次的中斷了,今天還是要講中斷,為啥呢?因為在作業系統中,中斷是必須要講的..       那麼什麼叫中斷呢, 中斷還是打斷,這樣一說你就不明白了。唉,中斷還真是有點像打斷。我們知道linux管理所有的硬體裝置,要做的第一件事先是通訊。然後,我們天天在說一句話:處理器的速度跟

10.Linux核心設計實現 P148---自旋鎖總結 (轉)

自旋鎖可分為用在單核處理器上和用在多核處理器上。單核處理器:用在單核處理器上,又可分為兩種:1.系統不支援核心搶佔此時自旋鎖什麼也不做,確實也不需要做什麼,因為單核處理器只有一個執行緒在執行,又不支援核心搶佔,因此資源不可能會被其他的執行緒訪問到。2.系統支援核心搶佔這種情況下,自旋鎖加鎖僅僅是禁止了核心搶佔