虛擬檔案系統,也不知道大家聽過沒有,反正我是聽過了!我們知道在電腦行業,很多東西都不是一定有個官方說:朋友,我最大,你們做的東西,都要是這個樣子,否則是非法的。事實上,很多東西都是靠的一種實力,通過實力來慢慢在人們心中成為既定事實。這個事實同樣是沒有官方的。好了,問題來了,沒有官方,就沒有標準,沒有標準就沒有統一,沒有統一那就是三國時代,混戰當道也!
怎麼辦?特別是百花爭鳴的檔案系統,這時linux的核心開發人員們想到了VFS(虛擬檔案系統)。VFS使得使用者可以直接使用open(),read()和write()這樣的系統調用而不用關注具體檔案系統和實際物理介質。也許你感覺不是很新奇啊,告訴你新奇的事情:在老式作業系統上(比如DOS),任何對非本地檔案系統的訪問都必須依靠特殊工具才能完成。這種實現的方式是核心在它的底層檔案系統介面上建立了一個抽象層。該抽象層是linux能夠支援各種檔案系統,即便是它們在功能和行為上存在很大差別。為了支援檔案系統,VFS提供了一個通用檔案系統模型,該模型囊括了我們所能想到的檔案系統的常用功能和行為。這個VFS抽象層之所以能銜接各種各樣的檔案系統,是因為它定義了所有檔案系統都支援的基本抽象介面和資料結構,同時實際系統也將自身的諸如“如何開啟檔案”,“目錄是什麼”等概念在形式上與VFS的定義保持一致。因為實際檔案系統的代碼在統一的介面和資料結構隱藏了具體的實現細節,所以在VFS層和核心的其他部分看來,所有檔案系統都是相同的,它們都支援像檔案和目錄這樣的概念,同時也支援像建立和刪除檔案這樣的操作。
實際檔案系統通過編程提供VFS所期望的抽象介面和資料結構,這樣,核心就可以毫不費力地和任何檔案系統協同工作。那麼接下的問題,它們直接的關係如何呢,看下邊的例子:
write(f,&buf,len);
該代碼不用說,應該明白。這個使用者調用首先被一個通用系統調用sys_write()處理,sys_write()函數要找到f所在的檔案系統實際給出的是哪個寫操作,然後再執行該操作。實際檔案系統的寫方法是檔案系統實現的一部分,資料最終通過該操作寫入介質。給出流程:
下面我就先從整體上對unix(linux)檔案系統做個概述,然後在具體下去。Unix使用了四種和檔案系統相關的傳統抽象概念,如下:
1.檔案:就是一個有序位元組串。 2.目錄項:檔案是放在目錄中,目錄又可以層層嵌套,形成檔案路徑。路徑中的每一項就叫做目錄項。目錄是檔案,這個檔案列出了該目錄下的所有檔案. 3.索引節點:一個檔案其實是由兩部分組成:相關資訊和檔案本身。這裡的相關資訊指的是存取控制許可權,大小,擁有者,建立時間等。檔案相關資訊也叫 做中繼資料,被儲存在一個單獨的資料結構中,這個結構就叫做索引點(index node,簡寫inode)。 4.安裝點(掛載點):檔案系統被安裝在一個特定的安裝點上,該安裝點在全域階層中被稱為命名空間,所有的已安裝檔案系統都作為根檔案樹的樹葉出 現在系統中。 5.超級塊:是一種包含檔案系統資訊的資料結構,裡邊是檔案系統的控制資訊。 |
對應於,我們知道VFS是介於使用者檔案和檔案系統之間的一個概念,所以如果檔案系統想要穿透VFS供使用者空間使用,就必須經過封裝,提供一個符合這些概念的介面。上述每個元素都對應一個對象,該對象有屬性結構體,描述了該對象的屬性。有操作結構體,包含了自身所支援的操作,下面詳細介紹:
1.超級塊對象:代表一個已安裝的檔案系統。由資料結構super_block結構體表示,定義在linux/fs.h中。如下所示:
struct super_block { struct list_head s_list; /* list of all superblocks */ dev_t s_dev; /* identifier */ unsigned long s_blocksize; /* block size in bytes */ unsigned long s_old_blocksize; /* old block size in bytes */ unsigned char s_blocksize_bits; /* block size in bits */ unsigned char s_dirt; /* dirty flag */ unsigned long long s_maxbytes; /* max file size */ struct file_system_type s_type; /* filesystem type */ struct super_operations s_op; /* superblock methods */ struct dquot_operations *dq_op; /* quota methods */ struct quotactl_ops *s_qcop; /* quota control methods */ struct export_operations *s_export_op; /* export methods */ unsigned long s_flags; /* mount flags */ unsigned long s_magic; /* filesystem's magic number */ struct dentry *s_root; /* directory mount point */ struct rw_semaphore s_umount; /* unmount semaphore */ struct semaphore s_lock; /* superblock semaphore */ int s_count; /* superblock ref count */ int s_syncing; /* filesystem syncing flag */ int s_need_sync_fs; /* not-yet-synced flag */ atomic_t s_active; /* active reference count */ void *s_security; /* security module */ struct list_head s_dirty; /* list of dirty inodes */ struct list_head s_io; /* list of writebacks */ struct hlist_head s_anon; /* anonymous dentries */ struct list_head s_files; /* list of assigned files */ struct block_device *s_bdev; /* associated block device */ struct list_head s_instances; /* instances of this fs */ struct quota_info s_dquot; /* quota-specific options */ char s_id[32]; /* text name */ void *s_fs_info; /* filesystem-specific info */ struct semaphore s_vfs_rename_sem; /* rename semaphore */};
建立,管理和銷毀超級塊對象的代碼位於檔案fs/super.c中,超級塊對象通過alloc_super()函數建立並初始化。在檔案系統安裝時,核心會調用該函數以便從磁碟讀取檔案系統超級塊,並且將其資訊填充到記憶體中的超級塊對象中。其中最重要的一個是s_op,指向超級塊的操作函數表,由super_operations結構體表示,定義在linux/fs.h中,如下:
struct super_operations { struct inode *(*alloc_inode) (struct super_block *sb); void (*destroy_inode) (struct inode *); void (*read_inode) (struct inode *); void (*dirty_inode) (struct inode *); void (*write_inode) (struct inode *, int); void (*put_inode) (struct inode *); void (*drop_inode) (struct inode *); void (*delete_inode) (struct inode *); void (*put_super) (struct super_block *); void (*write_super) (struct super_block *); int (*sync_fs) (struct super_block *, int); void (*write_super_lockfs) (struct super_block *); void (*unlockfs) (struct super_block *); int (*statfs) (struct super_block *, struct statfs *); int (*remount_fs) (struct super_block *, int *, char *); void (*clear_inode) (struct inode *); void (*umount_begin) (struct super_block *); int (*show_options) (struct seq_file *, struct vfsmount *);};
當檔案系統需要對其超級塊執行操作時,首先要在超級塊對象中尋找需要的操作方法。比如一個檔案系統要寫自己的超級塊,需要調用:sb->s_op->write_super(sb)這裡的sb是指向檔案系統超級塊的指標,沿著該指標進入超級塊操作函數表,並從表中取得希望得到的write_super()函數,該函數執行寫入超級塊的實際操作。
2.索引節點對象:由inode結構體表示,定義在linux/fs.h中,如下:
struct inode { struct hlist_node i_hash; /* hash list */ struct list_head i_list; /* list of inodes */ struct list_head i_dentry; /* list of dentries */ unsigned long i_ino; /* inode number */ atomic_t i_count; /* reference counter */ umode_t i_mode; /* access permissions */ unsigned int i_nlink; /* number of hard links */ uid_t i_uid; /* user id of owner */ gid_t i_gid; /* group id of owner */ kdev_t i_rdev; /* real device node */ loff_t i_size; /* file size in bytes */ struct timespec i_atime; /* last access time */ struct timespec i_mtime; /* last modify time */ struct timespec i_ctime; /* last change time */ unsigned int i_blkbits; /* block size in bits */ unsigned long i_blksize; /* block size in bytes */ unsigned long i_version; /* version number */ unsigned long i_blocks; /* file size in blocks */ unsigned short i_bytes; /* bytes consumed */ spinlock_t i_lock; /* spinlock */ struct rw_semaphore i_alloc_sem; /* nests inside of i_sem */ struct semaphore i_sem; /* inode semaphore */ struct inode_operations *i_op; /* inode ops table */ struct file_operations *i_fop; /* default inode ops */ struct super_block *i_sb; /* associated superblock */ struct file_lock *i_flock; /* file lock list */ struct address_space *i_mapping; /* associated mapping */ struct address_space i_data; /* mapping for device */ struct dquot *i_dquot[MAXQUOTAS]; /* disk quotas for inode */ struct list_head i_devices; /* list of block devices */ struct pipe_inode_info *i_pipe; /* pipe information */ struct block_device *i_bdev; /* block device driver */ unsigned long i_dnotify_mask; /* directory notify mask */ struct dnotify_struct *i_dnotify; /* dnotify */ unsigned long i_state; /* state flags */ unsigned long dirtied_when; /* first dirtying time */ unsigned int i_flags; /* filesystem flags */ unsigned char i_sock; /* is this a socket? */ atomic_t i_writecount; /* count of writers */ void *i_security; /* security module */ __u32 i_generation; /* inode version number */ union { void *generic_ip; /* filesystem-specific info */ } u;};
有時,某些檔案系統可能並不能完整的包含索引節點結構體要求的所有資訊。舉個例子,有的檔案系統可能並不記錄檔案的建立時間,這時,該檔案系統就可以在實現中選擇任意合適的辦法來解決這個問題,它可以在i_ctime中儲存0,或者讓i_ctime等於i_mtime,甚至任何其他值。索引節點對象中的inode_operations項存放了操作函數列表,定義在linux/fs.h中,如下:
struct inode_operations { int (*create) (struct inode *, struct dentry *,int); struct dentry * (*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, dev_t); int (*rename) (struct inode *, struct dentry *, struct inode *, struct dentry *); int (*readlink) (struct dentry *, char *, int); int (*follow_link) (struct dentry *, struct nameidata *); int (*put_link) (struct dentry *, struct nameidata *); void (*truncate) (struct inode *); int (*permission) (struct inode *, int); int (*setattr) (struct dentry *, struct iattr *); int (*getattr) (struct vfsmount *, struct dentry *, struct kstat *); int (*setxattr) (struct dentry *, const char *,const void *, size_t, int); ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t); ssize_t (*listxattr) (struct dentry *, char *, size_t); int (*removexattr) (struct dentry *, const char *);};
同樣,操作調用時,用以下方式:i->i_op->truncate(i).
由於版面原因,我不得不分兩次說了,下次繼續後面有關虛擬檔案系統的剩餘部分.