歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux綜合 >> Linux資訊 >> Linux文化 >> 虛擬文件系統

虛擬文件系統

日期:2017/2/27 12:14:51   编辑:Linux文化

本文檔中的慣例用法 <section>
==================
文檔中的每一節標題的右邊都有一個字符串"<section>"。
每個小節都會有個"<subsection>"在右邊。
這些字符串是為了在文檔中查詢更容易而設的。

注意:本文檔的最新更新可在下面找到:
http://www.atnf.csiro.au/~rgooch/linux/docs/vfs.txt


它到底是什麼? <section>
=============
Virtual File System(或者被稱為Virtual Filesystem Switch)是Linux內核中的一個軟件層,用於給用戶空間的程序提供文件系統接口。它也提供了內核中的一個抽象功能,允許不同的文件系統共存。


它的工作方式的概覽 <section>
==================
在這一節裡,在講解細節問題之前,我會簡單扼要的介紹一下VFS是如何工作的。首先,介紹一下當用戶程序打開或者操作文件時發生了些什麼,然後看看一個文件系統是如何被支持的。

打開一個文件 <subsection>
------------
VFS實現了open(2)系統調用。路徑參數被VFS用來在目錄入口緩存(dentry cache or "dcache")。這提供了一個將路徑名轉化為特定的dentry的一個快的查找機制。

一個單獨的dentry通常包含一個指向i節點(inode)的指針。i節點存在於磁盤驅動器上,它可以是一個規則文件,目錄,FIFO文件,等等。Dentry存在於RAM中,並且永遠不會被存到磁盤上:它們僅僅為了提高系統性能而存在。i節點存在於磁盤上,當需要時被拷入內存中,之後對它的任何改變將被寫回磁盤。存在於RAM中的i節點就是VFS的i節點,dentry所包含的指針指向的就是它。

dcache是你的整個文件空間的觀察點。跟Linus不同,我們中的大多數人不可能有足夠的RAM空間來放我們的文件空間的所有文件的目錄入口緩存(dentry),所以我們的dcache會有缺少的項。為了將路徑名轉換為一個dentry,VFS不得不采取創建dentry的方式,並在創建dentry時將指針指向相應的i節點。這是通過對i節點的查找完成的。

為了查找一個文件的i節點(通常從磁盤上讀),VFS需要調用該文件的父目錄的lookup()方法,此方法是特定的文件系統所設置的。後面對此將會有更詳盡的描述。

一旦VFS得到了所需要的dentry(同時也得到了相應的i節點),我們就能夠對文件做想要的操作:打開文件,或者用stat(2)來看i節點中的數據。stat(2)的操作非常簡單:在VFS得到dentry之後,它取得inode中的一些數據並將其中的一部分送回用戶空間。打開一個文件需要其它的操作:分配一個struct file(定義於linux/fs.h,這是內核中的文件描述)結構。新分配的struct file結構被指向dentry的指針和對文件進行操作的函數集合所初始化,這些都是從i節點中得到的。通過這種方式,特定的文件系統實現才能起作用。

文件結構(struct file)被放在進程的文件描述符表中。

讀,寫和關閉文件(或者其它的VFS操作)是通過使用用戶空間的文件描述符找到相應的文件結構(struct file),然後調用所需要的方法函數來實現的。

當文件處於打開狀態時,系統保持相應的dentry為"open"狀態(正在使用),這表示相應的i節點在被使用。


注冊和安裝一個文件系統 <subsection>
----------------------
如果你想在內核中支持一種新的文件系統的話,你所需要做的僅僅是調用函數register_filesystem().你向內核中傳遞一個描述文件系統實現的結構(struct filesystem), 此結構將被加入到內核的支持文件系統表中去。你可以運行下面的命令:
% cat /proc/filesystems
這樣可以看到你的系統支持哪些文件系統。

當一個mount請求出現時,VFS將會為特定的文件系統調用相應的方法。安裝點的dentry結構將會被改為指向新文件系統的根i節點。

現在是看看細節的時候了,nice to look!


struct file_system_type <section>
=======================
此結構描述了文件系統。在內核2.1.99中,此結構的定義如下:
(注:在2.2的內核中,此結構也沒有變化)
struct file_system_type {
const char *name;
int fs_flags;
struct super_block *(*read_super) (struct super_block *, void *, int);
struct file_system_type * next;
};

其中各個域的意義:
name:文件系統的類型名稱,如"vfat","ext2",等等。
fs_flags:變量標志,如FS_REQUIRES_DEV, FS_NO_DCACHE,等等.
read_super:當此種文件系統的一個新的實例要被安裝時,此方法會被調用。
next:被內部的VFS實現所使用,你只需要將其初試化為NULL。

函數read_super具有以下的參數:
struct super_block *sb:超級塊結構。此結構的一部分被VFS初始化,余下的部分必須被函數read_super初始化。
void * data:任意的安裝選項,通常是ASCII的字符串。
int silent:表示當出現錯誤時是否保持安靜。(不報警?)

read_super方法必須確定指定的塊設備是否包含了一個所支持的文件系統。當成功時返回超級塊結構的指針,錯誤時返回NULL。

read_super方法填充進超級塊結構(struct super_block)的最有用的域是"s_op"域。這是一個指向struct super_operations的指針,此結構描述了文件系統實現的下一層細節。

struct super_operations <section>
=======================
此結構描述了VFS對文件系統的超級塊所能進行的操作。
在內核2.1.99中,此結構的定義如下:
(注:在2.2的內核中,此結構已經有了改變)
struct super_operations {
void (*read_inode) (struct inode *);
void (*write_inode) (struct inode *);
void (*put_inode) (struct inode *);
void (*delete_inode) (struct inode *);
int (*notify_change) (struct dentry *, struct iattr *);
void (*put_super) (struct super_block *);
void (*write_super) (struct super_block *);
int (*statfs) (struct super_block *, struct statfs *, int);
int (*remount_fs) (struct super_block *, int *, char *);
void (*clear_inode) (struct inode *);
};

除非特別提出,所有的方法都在未加鎖的情況下被調用,這意味著大多數方法都可以安全的被阻塞。所有的方法都僅僅在進程空間被調用(例如,在中斷處理程序和底半部中不能調用它們)

read_inode:從一個文件系統中讀取一個特定的i節點時調用此方法。i節點中的域"i_ino"被VFS初始化為指向所讀的i節點,其余的域被此方法所填充。

write_inode:當VFS需要向磁盤上的一個i節點寫時調用。

put_inode:當VFS的i節點被從i節點緩沖池移走時被調用。此方法是可選的。

delete_inode:當VFS想刪除一個i節點時調用次方法。

notify_change:當VFS的i節點的屬性被改變時調用。若此域為NULL則VFS會調用rite_inode.此方法調用時需要鎖住內核。
put_super:當VFS要釋放超級塊時調用(umount一個文件系統).此方法調用時需要鎖住內核。

write_super:當VFS超級塊需要被寫入磁盤時被調用。此方法為可選的。

statfs:當VFS需要得到文件系統的統計數據時調用。此方法調用時需要鎖住內核。

remount_fs:當文件系統被重新安裝時調用。此方法調用時需要鎖住內核。

clear_inode:當VFS清除i節點時調用。可選項。

以上方法中,read_inode需要填充"i_op"域,此域為一個指向struct inode_operations結構的指針,它描述了能夠對一個單獨的i節點所能進行的操作。


struct inode_operations <section>
=======================
此結構描述了VFS能夠對文件系統的一個i節點所能進行的操作。
在內核2.1.99中,此結構的定義如下:
(注:在2.2的內核中,此結構已經有了少許改變)
struct inode_operations {
struct file_operations * default_file_ops;
int (*create) (struct inode *,struct dentry *,int);
int (*lookup) (struct inode *,struct dentry *);
int (*link) (struct dentry *,struct inode *,struct dentry *);
int (*unlink) (struct inode *,struct dentry *);
int (*symlink) (struct inode *,struct dentry *,const char *);
int (*mkdir) (struct inode *,struct dentry *,int);
int (*rmdir) (struct inode *,struct dentry *);
int (*mknod) (struct inode *,struct dentry *,int,int);
int (*rename) (struct inode *, struct dentry *,
struct inode *, struct dentry *);
int (*readlink) (struct dentry *, char *,int);
struct dentry * (*follow_link) (struct dentry *, struct dentry *);
int (*readpage) (struct file *, struct page *);
int (*writepage) (struct file *, struct page *);
int (*bmap) (struct inode *,int);
void (*truncate) (struct inode *);
int (*permission) (struct inode *, int);
int (*smap) (struct inode *,int);
int (*updatepage) (struct file *, struct page *, const char *,
unsigned long, unsigned int, int);
int (*revalidate) (struct dentry *);
};

default_file_ops:這是一個指向struct file_operations的指針,包含了對一個打開的文件所能進行的操作。

create:被open(2)和creat(2)所調用,僅僅在你要支持普通文件時才需要。參數中的dentry不應該包含有i節點的指針(即應該為一個negative dentry)。這裡你可能需要對傳入的dentry和i節點調用函數d_instantiate.

lookup:當VFS要在一個父目錄中查找一個i節點時調用。待查找的文件名在dentry中。此方法必須調用d_add函數把找到的i節點插入到dentry中,i節點的"i_count"域要加一。若指定的i節點不存在的話,一個NULL的i節點指針將被插入到dentry中去(這種情況的dentry被稱為negative dentry)。Returning an error code from this routine must only be done on a real error, otherwise creating inodes with system calls like create(2), mknod(2), mkdir(2) and so on will fail.If you wish to overload the dentry methods then you should initialise the "d_dop" field in the dentry; this is a pointer to a struct "dentry_operations".This method is called with the directory semaphore held。

link:被link(2)所調用。僅在你需要支持hard link時才需要它。跟create方法相同的原因,你可能在此方法中也需要調用d_instantiate()函數來驗證。

unlink:被unlink(2)所調用。僅在你要支持對i節點的刪除時才需要它。

symlink:被symlink(2)調用。僅在需要支持符號鏈接時才需要它。通上面兩處,你需要對傳入的參數進行驗證,要調用d_instantiate()函數。

mkdir:被mkdir(2)調用。僅在你要支持建立子目錄時才需要它。同上,你需要調用d_instantiate()函數進行驗證。

rmdir:被rmdir(2)所調用。僅在你要支持對子目錄的刪除時才需要它。

mknod:被mknod(2)所調用,用於建立一個設備i節點,或者FIFO,或socket.僅當你需要支持對這些類型的i節點的建立時才需要此方法。同上面幾個,你可能也需要調用_instantiate來驗證參數。

readlink:被readlink(2)調用。僅當你要支持對符號鏈接的讀取才需要它。

follow_link:被VFS調用,用以從一個符號鏈接找到相應的i節點。僅當你需要支持符號鏈接時才需要此方法。


struct file_operations <section>
======================
結構file_operations包含了VFS對一個已打開文件的操作。
在內核2.1.99中,此結構的定義如下:
(注:在2.2的內核中,此結構已經有了少許改變)
struct file_operations {
/*在VFS需要移動文件位置指針時被調用 */
loff_t (*llseek) (struct file *, loff_t, int);
/* 被read系統調用所使用 */
ssize_t (*read) (struct file *, char *, size_t, loff_t *);
/* 被write系統調用所使用 */
ssize_t (*write) (struct file *, const char *, size_t, 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);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, struct dentry *);
int (*fasync) (struct file *, int);
int (*check_media_change) (kdev_t dev);
int (*revalidate) (kdev_t dev);
int (*lock) (struct file *, int, struct file_lock *);
};

llseek:當VFS需要移動文件指針的位置時調用。

read:被read(2)所調用。

write:被write(2)所調用。

readdir:當VFS需要讀取目錄中的內容時被調用。

poll: called by the VFS when a process wants to check if there is activity on this file and (optionally) go to sleep until there is activity.
(注:這裡我怎麼想都翻不好,所以就把原文放在這裡了,poll就是相當於select的東西)

ioctl:被ioctl(2)所調用。

mmap:被mmap(2)所調用。

open:當VFS要打開一個i節點時調用它。當VFS打開一個文件時,它建立一個新的struct file結構,並用i節點中的"default_file_ops"來初始化其中的f_op域,然後對新分配的文件結構調用open方法。你可以認為open方法實際上屬於struct inode_operations。I think it's done the way it is because it makes filesystems simpler to implement.open方法是一個很好的初始化文件結構中的"private_data"域的的地方。

release:當沒有對被打開文件的引用時調用此方法。

fsync:被fsync(2)所調用。

fasync:當用fcntl(2)激活一個文件的異步模式時此方法被調用。

這些文件操作是由i節點所在的特定文件系統所實現的。當打開一個設備節點時(字符或塊設備特殊文件),大多數文件系統會調用VFS中的特定支持例程,由此來找到所需要的設備驅動信息;

這些支持例程用設備驅動程序的方法來代替文件系統的文件操作,然後繼續對文件調用新的open方法。這就是為什麼當你打開文件系統上的一個設備特殊文件時,最後被調用的卻是設備驅動程序的open方法。另外,devfs(Device Filesystem)有一個從設備節點到設備驅動程序的更直接的方式(這是非官方的內核補丁)


struct dentry_operations <section>
========================
This describes how a filesystem can overload the standard dentry
operations.Dentries和dcache是屬於VFS和單個文件系統實現的,設備驅動與此無關。
在內核2.1.99中,此結構的定義如下:
(注:在2.2的內核中,此結構沒有改變)
struct dentry_operations {
int (*d_revalidate)(struct dentry *);
int (*d_hash) (struct dentry *, struct qstr *);
int (*d_compare) (struct dentry *, struct qstr *, struct qstr *);
void (*d_delete)(struct dentry *);
void (*d_release)(struct dentry *);
void (*d_iput)(struct dentry *, struct inode *);
};

d_revalidate:當VFS要使一個dentry重新生效時被調用。

d_hash:當VFS向哈希表中加入一個dentry時被調用。

d_compare:當指向一個dentry的最後的引用被去除時此方法被調用,因為這意味這沒有人在使用此dentry;當然,此dentry仍然有效,並且仍然在dcache中。

d_release: 當一個dentry被清除時調用此方法。

d_iput:當一個dentry釋放它的i節點時(在dentry被清除之前)此方法被調用。The default when this is NULL is that the VFS calls iput(). If you define this method, you must call iput() yourself.

每個dentry都有一個指向其父目錄dentry的指針,一個子dentry的哈希列表。子dentry基本上就是目錄中的文件。

dget:為一個已經存在的dentry打開一個新的句柄(這僅僅增加引用計數)

dput:關閉一個dentry的句柄(減少引用計數).如果引用計數減少為0,d_delete方法將會被調用;並且,如果此dentry仍然在其父目錄的哈希列表中的話,此dentry將被放置於一個未被使用的列表中。將dentry放置於未使用表中意味著當系統需要更多的RAM時,將會遍歷未使用的dentry的列表,並回收其內存空間。假如當detry的引用計數為0時,它已經沒有在父目錄的哈希表中的話,在d_delete方法被調用之後系統就會回收起內存空間。

d_drop: 此方法將一個dentry從其父目錄的哈希列表中去掉。如果被去掉的dentry的引用計數降為0的話,系統會馬上調用d_put來去掉此dentry.

d_delete:刪除一個dentry.如果沒有別的對此dentry的打開引用的話,此dentry會變成一個negative dentry(d_iput方法會被調用);如果有別的對此dentry的引用的話,將會調用d_drop.

d_add:向父目錄的哈希列表中加入一個dentry,然後調用d_instantiate().

d_instantiate:把一個dentry加入別名哈希列表中,並更新其d_inode域為所給的i節點。i節點中的i_count域加一。假如i節點的指針為NULL,此dentry就被稱為"negative dentry".此函數通常在為一個已存在的negative dentry建立i節點時被調用。


注:第一次做翻譯,可能有很多地方做得不好,還希望大家多批評指正。我以前看過很多前輩的翻譯文章,從中獲益匪淺,也很感激這些前輩們的無私奉獻。現在自己能看懂一些東西了,也希望自己能為別人盡一份力,正好我的畢業設計又跟這個有點關系,於是就有了這篇譯文,不過水平太差,大家不要扔雞蛋番茄。有些地方我覺得實在翻不好,就把原文放上去了,實在是能力有限。如果有什麼錯誤或者疏漏的話,請mail:[email protected].希望能對別人有幫助而不是誤導。


Copyright © Linux教程網 All Rights Reserved