用戶(hù)視角下的文件系統(tǒng)
??? “一切皆是文件”,是UNIX和Linux的基本哲學(xué)之一。Linux對(duì)于文件I/O操作,實(shí)現(xiàn)了POSIX.1和Single UNIX Specification中的接口,包括open()、read()、write()、lseek()和close()等方法。正是由于Linux所實(shí)現(xiàn)的虛擬文件系統(tǒng)對(duì)具體文件系統(tǒng)進(jìn)行了抽象,使得Linux可以方便地實(shí)現(xiàn)文件I/O操作接口。用戶(hù)視角下的文件系統(tǒng),就是一組系統(tǒng)調(diào)用接口,其與VFS的關(guān)系如下:
??? 每個(gè)進(jìn)程在用戶(hù)空間內(nèi)都有一張file description table,用于描述已打開(kāi)的文件。當(dāng)open()成功返回時(shí),將返回文件描述符(file description),被插入到file description table中。
??? 如下圖所示,當(dāng)用戶(hù)進(jìn)程調(diào)用write()方法讀取文件時(shí),將調(diào)用VFS的sys_write()方法,而在sys_write()方法中調(diào)用文件系統(tǒng)接口的具體方法進(jìn)行硬盤(pán)讀取。在Linux2.6.25以后,sys_write()為vfs_write()所替代。
??? vfs_write()源代碼如下:
??? 由此可見(jiàn),write的調(diào)用過(guò)程為:→write()→vfs_write()→file->f_op->write(),由文件系統(tǒng)提供的VFS API進(jìn)行實(shí)際的存取操作。
硬盤(pán)視角下的文件系統(tǒng)
??? Linux在硬盤(pán)上的文件系統(tǒng)與邏輯上的文件系統(tǒng)VFS完全不同。UFS(UNIX File System)基于Berkeley fast file system,如下:
??? UFS由許多分區(qū)構(gòu)成,可以允許分區(qū)之間采取不同的文件系統(tǒng),但同一個(gè)分區(qū)之內(nèi)必須為同一文件系統(tǒng)。上圖 啟動(dòng)塊 (Boot Block)大小確定,為1KB,由PC標(biāo)準(zhǔn)規(guī)定,用來(lái)存儲(chǔ)磁盤(pán)分區(qū)信息和啟動(dòng)信息,任何文件系統(tǒng)都不能使用啟動(dòng)塊。UFS文件系統(tǒng)將整個(gè)分區(qū)劃分成超級(jí)塊(Super Block,除塊組0之外的Super Block都為備份)、塊描述符表、i-node位圖、塊位圖、i-node表、data數(shù)據(jù)塊。
??? 超級(jí)塊 包含了關(guān)于該硬盤(pán)或分區(qū)文件系統(tǒng)的整體信息,如文件系統(tǒng)大小等。 索引結(jié)點(diǎn) ,包含了針對(duì)某一具體文件幾乎的全部信息,如文件存取權(quán)限、所有者、大小、建立時(shí)間以及對(duì)應(yīng)的目錄塊和數(shù)據(jù)塊等。 數(shù)據(jù)塊 是真正存儲(chǔ)文件內(nèi)容的位置,但索引結(jié)點(diǎn)中不包括文件名,文件名存于目錄塊。 目錄塊 里包含文件名以及文件索引結(jié)點(diǎn)編號(hào)。
??? 上圖中,位于數(shù)據(jù)塊中存儲(chǔ)目錄數(shù)據(jù)的directory entry均指向同一個(gè)i-node,而i-node中包括三個(gè)data block。
內(nèi)核虛擬文件系統(tǒng)VFS
內(nèi)核文件系統(tǒng)主要的四個(gè)數(shù)據(jù)結(jié)構(gòu)為:
??? superblock,代表一個(gè)具體的已掛載的文件系統(tǒng);
??? inode,代表一個(gè)具體的文件;
??? dentry,代表一個(gè)目錄項(xiàng),如/home/icanth,home和icanth都是一個(gè)目錄項(xiàng);
??? file,代表一個(gè)進(jìn)程已經(jīng)打開(kāi)的文件。
圖 super_block、file、dentry和inode的關(guān)系
??? 每個(gè)file結(jié)構(gòu)體都指向一個(gè)file_operations結(jié)構(gòu)體,這個(gè)結(jié)構(gòu)體的成員都是函數(shù)指針,指向?qū)崿F(xiàn)各種文件操作的內(nèi)核函數(shù)。比如在用戶(hù)程序中read一個(gè)文件描述符,read通過(guò)系統(tǒng)調(diào)用進(jìn)入內(nèi)核,然后找到這個(gè)文件描述符所指向的file結(jié)構(gòu)體,找到file結(jié)構(gòu)體所指向的file_operations結(jié)構(gòu)體,調(diào)用它的read成員所指向的內(nèi)核函數(shù)以完成用戶(hù)請(qǐng)求。在用戶(hù)程序中調(diào)用lseek、read、write、ioctl、open等函數(shù),最終都由內(nèi)核調(diào)用file_operations的各成員所指向的內(nèi)核函數(shù)完成用戶(hù)請(qǐng)求。file_operations結(jié)構(gòu)體中的release成員用于完成用戶(hù)程序的close請(qǐng)求,之所以叫release而不叫close是因?yàn)樗灰欢ㄕ娴年P(guān)閉文件,而是減少引用計(jì)數(shù),只有引用計(jì)數(shù)減到0才關(guān)閉文件。對(duì)于同一個(gè)文件系統(tǒng)上打開(kāi)的常規(guī)文件來(lái)說(shuō),read、write等文件操作的步驟和方法應(yīng)該是一樣的,調(diào)用的函數(shù)應(yīng)該是相同的,所以圖中的三個(gè)打開(kāi)文件的file結(jié)構(gòu)體指向同一個(gè)file_operations結(jié)構(gòu)體。如果打開(kāi)一個(gè)字符設(shè)備文件,那么它的read、write操作肯定和常規(guī)文件不一樣,不是讀寫(xiě)磁盤(pán)的數(shù)據(jù)塊而是讀寫(xiě)硬件設(shè)備,所以file結(jié)構(gòu)體應(yīng)該指向不同的file_operations結(jié)構(gòu)體,其中的各種文件操作函數(shù)由該設(shè)備的驅(qū)動(dòng)程序?qū)崿F(xiàn)。
??? 每個(gè)file結(jié)構(gòu)體都有一個(gè)指向dentry結(jié)構(gòu)體的指針,“dentry”是directory entry(目錄項(xiàng))的縮寫(xiě)。我們傳給open、stat等函數(shù)的參數(shù)的是一個(gè)路徑,例如/home/akaedu/a,需要根據(jù)路徑找到文件的inode。為了減少讀盤(pán)次數(shù),內(nèi)核緩存了目錄的樹(shù)狀結(jié)構(gòu),稱(chēng)為dentry cache,其中每個(gè)節(jié)點(diǎn)是一個(gè)dentry結(jié)構(gòu)體,只要沿著路徑各部分的dentry搜索即可,從根目錄/找到home目錄,然后找到akaedu目錄,然后找到文件a。dentry cache只保存最近訪問(wèn)過(guò)的目錄項(xiàng),如果要找的目錄項(xiàng)在cache中沒(méi)有,就要從磁盤(pán)讀到內(nèi)存中。
??? 每個(gè)dentry結(jié)構(gòu)體都有一個(gè)指針指向inode結(jié)構(gòu)體。inode結(jié)構(gòu)體保存著從磁盤(pán)inode讀上來(lái)的信息。在上圖的例子中,有兩個(gè)dentry,分別表示/home/akaedu/a和/home/akaedu/b,它們都指向同一個(gè)inode,說(shuō)明這兩個(gè)文件互為硬鏈接。inode結(jié)構(gòu)體中保存著從磁盤(pán)分區(qū)的inode讀上來(lái)信息,例如所有者、文件大小、文件類(lèi)型和權(quán)限位等。每個(gè)inode結(jié)構(gòu)體都有一個(gè)指向inode_operations結(jié)構(gòu)體的指針,后者也是一組函數(shù)指針指向一些完成文件目錄操作的內(nèi)核函數(shù)。和file_operations不同,inode_operations所指向的不是針對(duì)某一個(gè)文件進(jìn)行操作的函數(shù),而是影響文件和目錄布局的函數(shù),例如添加刪除文件和目錄、跟蹤符號(hào)鏈接等等,屬于同一文件系統(tǒng)的各inode結(jié)構(gòu)體可以指向同一個(gè)inode_operations結(jié)構(gòu)體。
??? inode結(jié)構(gòu)體有一個(gè)指向super_block結(jié)構(gòu)體的指針。super_block結(jié)構(gòu)體保存著從磁盤(pán)分區(qū)的超級(jí)塊讀取的信息,例如文件系統(tǒng)類(lèi)型、塊大小等。super_block結(jié)構(gòu)體的s_root成員是一個(gè)指向dentry的指針,表示這個(gè)文件系統(tǒng)的根目錄被mount到哪里,在上圖的例子中這個(gè)分區(qū)被mount到/home目錄下。
超級(jí)塊對(duì)象superblock
??? superblock是在<linux/fs.h>在下定義的結(jié)構(gòu)體super_block.
struct super_block { //超級(jí)塊數(shù)據(jù)結(jié)構(gòu)
??????? struct list_head s_list;??????????????? /*指向超級(jí)塊鏈表的指針*/
??????? ……
??????? struct file_system_type? *s_type;?????? /*文件系統(tǒng)類(lèi)型*/
?????? struct super_operations? *s_op;???????? /*超級(jí)塊方法*/
??????? ……
struct list_head s_instances; /* 該類(lèi)型文件系統(tǒng)*/
??????? ……
};
?
struct super_operations { //超級(jí)塊方法
??????? ……
??????? //該函數(shù)在給定的超級(jí)塊下創(chuàng)建并初始化一個(gè)新的索引節(jié)點(diǎn)對(duì)象
??????? struct inode *(*alloc_inode)(struct super_block *sb);
?????? ……
??????? //該函數(shù)從磁盤(pán)上讀取索引節(jié)點(diǎn),并動(dòng)態(tài)填充內(nèi)存中對(duì)應(yīng)的索引節(jié)點(diǎn)對(duì)象的剩余部分
void (*read_inode) (struct inode *);
?????? ……
};
索引結(jié)點(diǎn)對(duì)象inode
??? 索引節(jié)點(diǎn)對(duì)象存儲(chǔ)了文件的相關(guān)信息,代表了存儲(chǔ)設(shè)備上的一個(gè)實(shí)際的物理文件。當(dāng)一個(gè) 文件首次被訪問(wèn)時(shí),內(nèi)核會(huì)在內(nèi)存中組裝相應(yīng)的索引節(jié)點(diǎn)對(duì)象,以便向內(nèi)核提供對(duì)一個(gè)文件進(jìn)行操 作時(shí)所必需的全部信息;這些信息一部分存儲(chǔ)在磁盤(pán)特定位置,另外一部分是在加載時(shí)動(dòng)態(tài)填充的。
struct inode {//索引節(jié)點(diǎn)結(jié)構(gòu)
????? ……
????? struct inode_operations? *i_op;???? /*索引節(jié)點(diǎn)操作表*/
???? struct file_operations?? *i_fop;???? /*該索引節(jié)點(diǎn)對(duì)應(yīng)文件的文件操作集*/
???? struct super_block?????? *i_sb;???? /*相關(guān)的超級(jí)塊*/
???? ……
};
?
struct inode_operations { //索引節(jié)點(diǎn)方法
???? ……
???? //該函數(shù)為dentry對(duì)象所對(duì)應(yīng)的文件創(chuàng)建一個(gè)新的索引節(jié)點(diǎn),主要是由open()系統(tǒng)調(diào)用來(lái)調(diào)用
???? int (*create) (struct inode *,struct dentry *,int, struct nameidata *);
?
???? //在特定目錄中尋找dentry對(duì)象所對(duì)應(yīng)的索引節(jié)點(diǎn)
???? struct dentry * (*lookup) (struct inode *,struct dentry *, struct nameidata *);
???? ……
};
目錄項(xiàng)對(duì)象 dentry
??? 引入目錄項(xiàng)的概念主要是出于方便查找文件的目的。一個(gè)路徑的各個(gè)組成部分,不管是目錄還是 普通的文件,都是一個(gè)目錄項(xiàng)對(duì)象。如,在路徑/home/source/test.c中,目錄 /, home, source和文件 test.c都對(duì)應(yīng)一個(gè)目錄項(xiàng)對(duì)象。不同于前面的兩個(gè)對(duì)象,目錄項(xiàng)對(duì)象沒(méi)有對(duì)應(yīng)的磁盤(pán)數(shù)據(jù)結(jié)構(gòu),VFS在遍 歷路徑名的過(guò)程中現(xiàn)場(chǎng)將它們逐個(gè)地解析成目錄項(xiàng)對(duì)象。
struct dentry {//目錄項(xiàng)結(jié)構(gòu)
???? ……
???? struct inode *d_inode;?????????? /*相關(guān)的索引節(jié)點(diǎn)*/
??? struct dentry *d_parent;???????? /*父目錄的目錄項(xiàng)對(duì)象*/
??? struct qstr d_name;????????????? /*目錄項(xiàng)的名字*/
??? ……
???? struct list_head d_subdirs;????? /*子目錄*/
??? ……
???? struct dentry_operations *d_op;? /*目錄項(xiàng)操作表*/
??? struct super_block *d_sb;??????? /*文件超級(jí)塊*/
??? ……
};
?
struct dentry_operations {
??? //判斷目錄項(xiàng)是否有效;
??? int (*d_revalidate)(struct dentry *, struct nameidata *);
??? //為目錄項(xiàng)生成散列值;
??? int (*d_hash) (struct dentry *, struct qstr *);
??? ……
};
文件 對(duì)象 file
struct file {
??? ……
???? struct list_head??????? f_list;??????? /*文件對(duì)象鏈表*/
??? struct dentry????????? *f_dentry;?????? /*相關(guān)目錄項(xiàng)對(duì)象*/
??? struct vfsmount??????? *f_vfsmnt;?????? /*相關(guān)的安裝文件系統(tǒng)*/
??? struct file_operations *f_op;?????????? /*文件操作表*/
??? ……
};
?
struct file_operations {
??? ……
??? //文件讀操作
??? ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
??? ……
??? //文件寫(xiě)操作
??? ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
??? ……
??? int (*readdir) (struct file *, void *, filldir_t);
??? ……
??? //文件打開(kāi)操作
??? int (*open) (struct inode *, struct file *);
??? ……
};
文件系統(tǒng)對(duì)象
??? 根據(jù)文件系統(tǒng)所在的物理介質(zhì)和數(shù)據(jù)在物理介質(zhì)上的組織方式來(lái)區(qū)分不同的文件系統(tǒng)類(lèi)型的。 file_system_type結(jié)構(gòu)用于描述具體的文件系統(tǒng)的類(lèi)型信息。被Linux支持的文件系統(tǒng),都有且僅有一 個(gè)file_system_type結(jié)構(gòu)而不管它有零個(gè)或多個(gè)實(shí)例被安裝到系統(tǒng)中。而與此對(duì)應(yīng)的是每當(dāng)一個(gè)文件系統(tǒng)被實(shí)際安裝,就有一個(gè)vfsmount結(jié)構(gòu)體被創(chuàng)建,這個(gè)結(jié)構(gòu)體對(duì)應(yīng)一個(gè)安裝點(diǎn)。
struct file_system_type {
??????? const char *name;??????????????? /*文件系統(tǒng)的名字*/
??????? struct subsystem subsys;???????? /*sysfs子系統(tǒng)對(duì)象*/
??????? int fs_flags;??????????????????? /*文件系統(tǒng)類(lèi)型標(biāo)志*/
?
??????? /*在文件系統(tǒng)被安裝時(shí),從磁盤(pán)中讀取超級(jí)塊,在內(nèi)存中組裝超級(jí)塊對(duì)象*/
??????? struct super_block *(*get_sb) (struct file_system_type*,
??????????????????????????????????????? int, const char*, void *);
??????????
??????? void (*kill_sb) (struct super_block *);? /*終止訪問(wèn)超級(jí)塊*/???????????
??????? struct module *owner;??????????????????? /*文件系統(tǒng)模塊*/
??????? struct file_system_type * next;????????? /*鏈表中的下一個(gè)文件系統(tǒng)類(lèi)型*/
??????? struct list_head fs_supers;????????????? /*具有同一種文件系統(tǒng)類(lèi)型的超級(jí)塊對(duì)象鏈表*/
};
?
struct vfsmount
{
??????? struct list_head mnt_hash;?????????????? /*散列表*/
??????? struct vfsmount *mnt_parent;???????????? /*父文件系統(tǒng)*/
??????? struct dentry *mnt_mountpoint;?????????? /*安裝點(diǎn)的目錄項(xiàng)對(duì)象*/
??????? struct dentry *mnt_root;???????????????? /*該文件系統(tǒng)的根目錄項(xiàng)對(duì)象*/
??????? struct super_block *mnt_sb;????????????? /*該文件系統(tǒng)的超級(jí)塊*/
??????? struct list_head mnt_mounts;???????????? /*子文件系統(tǒng)鏈表*/
??????? struct list_head mnt_child;????????????? /*子文件系統(tǒng)鏈表*/
??????? atomic_t mnt_count;????????????????????? /*使用計(jì)數(shù)*/
??????? int mnt_flags;?????????????????????????? /*安裝標(biāo)志*/
??????? char *mnt_devname;?????????????????????? /*設(shè)備文件名*/
??????? struct list_head mnt_list;?????????????? /*描述符鏈表*/
??????? struct list_head mnt_fslink;???????????? /*具體文件系統(tǒng)的到期列表*/
??????? struct namespace *mnt_namespace;???????? /*相關(guān)的名字空間*/
};
打開(kāi)文件的流程
??? 由于sys_open()的代碼量大,函數(shù)調(diào)用關(guān)系復(fù)雜,以下主要是對(duì)該函數(shù)做整體的解析;而對(duì)其中的一些關(guān)鍵點(diǎn),則列出其關(guān)鍵代碼。
a. 從sys_open()的函數(shù)調(diào)用關(guān)系圖可以看到 ,sys_open()在做了一些簡(jiǎn)單的參數(shù)檢驗(yàn)后,就把接力棒傳給do_sys_open():
??? 1)首先,get_unused_fd()得到一個(gè)可用的文件描述符;通過(guò)該函數(shù),可知文件描述符實(shí)質(zhì)是進(jìn)程打開(kāi)文件列表中對(duì)應(yīng)某個(gè)文件對(duì)象的索引值;
??? 2)接著,do_filp_open()打開(kāi)文件,返回一個(gè)file對(duì)象,代表由該進(jìn)程打開(kāi)的一個(gè)文件;進(jìn)程通過(guò)這樣的一個(gè)數(shù)據(jù)結(jié)構(gòu)對(duì)物理文件進(jìn)行讀寫(xiě)操作。
??? 3)最后,fd_install()建立文件描述符與file對(duì)象的聯(lián)系,以后進(jìn)程對(duì)文件的讀寫(xiě)都是通過(guò)操縱該文件描述符而進(jìn)行。
b. do_filp_open()用于打開(kāi)文件 ,返回一個(gè)file對(duì)象;而打開(kāi)之前需要先找到該文件:
??? 1)open_namei()用于根據(jù)文件路徑名查找文件,借助一個(gè)持有路徑信息的數(shù)據(jù)結(jié)構(gòu)nameidata而進(jìn)行;
??? 2)查找結(jié)束后將填充有路徑信息的nameidata返回給接下來(lái)的函數(shù)nameidata_to_filp()從而得到最終的file對(duì)象;當(dāng)達(dá)到目的后,nameidata這個(gè)數(shù)據(jù)結(jié)構(gòu)將會(huì)馬上被釋放。
c.open_namei()用于查找一個(gè)文件:
??? 1)path_lookup_open()實(shí)現(xiàn)文件的查找功能;要打開(kāi)的文件若不存在,還需要有一個(gè)新建的過(guò)程,則調(diào)用path_lookup_create(),后者和前者封裝的是同一個(gè)實(shí)際的路徑查找函數(shù),只是參數(shù)不一樣,使它們?cè)谔幚砑?xì)節(jié)上有所偏差;
??? 2)當(dāng)是以新建文件的方式打開(kāi)文件時(shí),即設(shè)置了O_CREAT標(biāo)識(shí)時(shí)需要?jiǎng)?chuàng)建一個(gè)新的索引節(jié)點(diǎn),代表創(chuàng)建一個(gè)文件。在vfs_create()里的一句核心語(yǔ)句dir->i_op->create(dir, dentry, mode, nd)可知它調(diào)用了具體的文件系統(tǒng)所提供的創(chuàng)建索引節(jié)點(diǎn)的方法。注意:這邊的索引節(jié)點(diǎn)的概念,還只是位于內(nèi)存之中,它和磁盤(pán)上的物理的索引節(jié)點(diǎn)的關(guān)系就像位于內(nèi)存中和位于磁盤(pán)中的文件一樣。此時(shí)新建的索引節(jié)點(diǎn)還不能完全標(biāo)志一個(gè)物理文件的成功創(chuàng)建,只有當(dāng)把索引節(jié)點(diǎn)回寫(xiě)到磁盤(pán)上才是一個(gè)物理文件的真正創(chuàng)建。想想我們以新建的方式打開(kāi)一個(gè)文件,對(duì)其讀寫(xiě)但最終沒(méi)有保存而關(guān)閉,則位于內(nèi)存中的索引節(jié)點(diǎn)會(huì)經(jīng)歷從新建到消失的過(guò)程,而磁盤(pán)卻始終不知道有人曾經(jīng)想過(guò)創(chuàng)建一個(gè)文件,這是因?yàn)樗饕?jié)點(diǎn)沒(méi)有回寫(xiě)的緣故。
??? 3)path_to_nameidata()填充nameidata數(shù)據(jù)結(jié)構(gòu);
??? 4)may_open()檢查是否可以打開(kāi)該文件;一些文件如鏈接文件和只有寫(xiě)權(quán)限的目錄是不能被打開(kāi)的,先檢查nd->dentry->inode所指的文件是否是這一類(lèi)文件,是的話則錯(cuò)誤返回。還有一些文件是不能以TRUNC的方式打開(kāi)的,若nd->dentry->inode所指的文件屬于這一類(lèi),則顯式地關(guān)閉TRUNC標(biāo)志位。接著如果有以TRUNC方式打開(kāi)文件的,則更新nd->dentry->inode的信息。
在Linux3.2中,do_file_open->do_sys_open->do_filp_open->path_openat(nameidata)->link_path_work(pathname, nd), file = do_last
??? 查找路徑的過(guò)程定義在link_path_work中,主要傳入nameidata對(duì)象
struct nameidata { struct path path; struct qstr last; struct path root; struct inode *inode; /* path.dentry.d_inode */ unsigned int flags; unsigned seq; int last_type; unsigned depth; char *saved_names[MAX_NESTED_LINKS + 1]; /* Intent data */ union { struct open_intent open; } intent; }; /* * Name resolution. * This is the basic name resolution function, turning a pathname into * the final dentry. We expect 'base' to be positive and a directory. * * Returns 0 and nd will have valid dentry and mnt on success. * Returns error and drops reference to input namei data on failure. */ static int link_path_walk(const char *name, struct nameidata *nd) { struct path next; int err; while (*name=='/') name++; if (!*name) return 0; /* At this point we know we have a real path component. */ for(;;) { unsigned long hash; struct qstr this; unsigned int c; int type; err = may_lookup(nd); if (err) break; this.name = name; c = *(const unsigned char *)name; hash = init_name_hash(); do { name++; hash = partial_name_hash(c, hash); c = *(const unsigned char *)name; } while (c && (c != '/')); this.len = name - (const char *) this.name; this.hash = end_name_hash(hash); type = LAST_NORM; if (this.name[0] == '.') switch (this.len) { case 2: if (this.name[1] == '.') { type = LAST_DOTDOT; nd->flags |= LOOKUP_JUMPED; } break; case 1: type = LAST_DOT; } if (likely(type == LAST_NORM)) { struct dentry *parent = nd->path.dentry; nd->flags &= ~LOOKUP_JUMPED; if (unlikely(parent->d_flags & DCACHE_OP_HASH)) { err = parent->d_op->d_hash(parent, nd->inode, &this); if (err < 0) break; } } /* remove trailing slashes? */ if (!c) goto last_component; while (*++name == '/'); if (!*name) goto last_component; err = walk_component(nd, &next, &this, type, LOOKUP_FOLLOW); if (err < 0) return err; if (err) { err = nested_symlink(&next, nd); if (err) return err; } if (can_lookup(nd->inode)) continue; err = -ENOTDIR; break; /* here ends the main loop */ last_component: nd->last = this; nd->last_type = type; return 0; } terminate_walk(nd); return err; }
一個(gè)簡(jiǎn)單的文件系統(tǒng)實(shí)現(xiàn)——XORFS
??? xorfs.c
#include <linux/kernel.h> #include <linux/init.h> #include <linux/module.h> #include <linux/pagemap.h> /* PAGE_CACHE_SIZE */ #include <linux/fs.h> /* This is where libfs stuff is declared */ #include <asm/atomic.h> #include <asm/uaccess.h> /* copy_to_user */ #include <linux/pagemap.h> #include <linux/buffer_head.h> /* * Wen Hui. * Just for linux 2.6 * * desp: * innux 3.2, get_sb --> mount, * get_sb_single --> mount_single. */ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Wen Hui"); #define XORFS_MAGIC 0x20120418 #define FILE_INODE_NUMBER 2 /** * system_file_type */ /* get_sb */ static struct super_block * xorfs_get_sb(struct file_system_type *fs_type, int flags, const char *devname, void *data, struct vfsmount* mnt); /* kill_sb */ static void xorfs_kill_sb(struct super_block *super); static int xorfs_fill_super(struct super_block* sb, void *data, int silent); /** * super_operations */ static int xorfs_super_write_inode(struct inode *inode, struct writeback_control *wbc); /*static void xorfs_super_read_inode(struct inode *inode);*/ /** * inode_operations */ /* lookup */ static struct dentry* xorfs_inode_lookup(struct inode*, struct dentry *, struct nameidata *); static struct inode* xorfs_iget(struct super_block *sp, unsigned long ino); /** * file_operations */ static int xorfs_file_open(struct inode *inode, struct file *file); static int xorfs_file_readdir(struct file *file, void *dirent, filldir_t filldir); static int xorfs_file_release(struct inode* ino, struct file *file); static ssize_t xorfs_file_read(struct file *file, char *buf, size_t max, loff_t* offset); static ssize_t xorfs_file_write(struct file *file, const char *buf, size_t max, loff_t* offset); /** * address_space_operations */ /* readpage * old version: ->prepare_write(),->commit_write(), * ->sync_page(),and ->readpage() * new version (LSF'08'): try use vm_operations * instead of address_space_operations, * and a small/dummpy ->readpage is still needed because * ->generic_file_mmap, still check * for the existence of the ->readpage method. */ static int xorfs_readpage(struct file *file, struct page *page); /* write page */ static int xorfs_writepage(struct page *page, struct writeback_control *wbc); /* write_beign */ static int xorfs_write_begin(struct file *file, struct address_space *mapping, loff_t pos, unsigned len, unsigned flags, struct page **pagep, void **fsdata); /* write_end */ static int xorfs_write_end(struct file *file, struct address_space *mapping, loff_t pos, unsigned len, unsigned copied, struct page *page, void *fsdata); /* * Data declarations */ static struct super_operations xorfs_sops = { // .read_inode = xorfs_super_read_inode .statfs = simple_statfs, .write_inode = &xorfs_super_write_inode }; // struct xorfs_sops static struct inode_operations xorfs_iops = { .lookup = xorfs_inode_lookup }; // struct xorfs_iops static struct file_operations xorfs_fops = { .open = xorfs_file_open, .read = &xorfs_file_read, .readdir = &xorfs_file_readdir, .write = &xorfs_file_write, .release = &xorfs_file_release, .fsync = &generic_file_fsync }; // struct xorfs_fops static struct file_system_type xorfs = { name : "xorfs", get_sb : xorfs_get_sb, kill_sb : xorfs_kill_sb, owner : THIS_MODULE }; // struct file_system_type static struct address_space_operations xorfs_aops = { .readpage = xorfs_readpage, .writepage = xorfs_writepage, .write_begin = xorfs_write_begin, .write_end = xorfs_write_end }; // struct xorfs_aops static struct inode *xorfs_root_inode; static char file_buf[PAGE_SIZE] = "Hello World\n"; static int file_size = 12; /** * system_file_type */ static struct super_block * xorfs_get_sb(struct file_system_type *fs_type, int flags, const char *devname, void *data, struct vfsmount* mnt) { printk("XORFS:xorfs_get_sb\n"); return get_sb_single(fs_type, flags, data, &xorfs_fill_super, mnt); } // xorfs_get_sb static void xorfs_kill_sb(struct super_block *super) { printk("XORFS: xorfs_kill_sb\n"); kill_anon_super(super); } // xorfs_kill_sb /* call the get_sb_single(), and callback the fn() */ static int xorfs_fill_super(struct super_block* sb, void *data, int silent) { printk("XORFS: xorfs_fill_super\n"); sb->s_blocksize = 1024; sb->s_blocksize_bits = 10; sb->s_magic = XORFS_MAGIC; sb->s_op = &xorfs_sops; // super block operation sb->s_type = &xorfs; // file_system_type // xorfs_root_inode = iget_locked(sb, 1); // allocate 1 node xorfs_root_inode = xorfs_iget(sb, 1); // allocate 1 node xorfs_root_inode->i_op = &xorfs_iops; // set the inode ops xorfs_root_inode->i_mode = S_IFDIR | S_IRWXU; xorfs_root_inode->i_fop = &xorfs_fops; // set the inode file operations // xorfs_root_inode->i_mapping->a_ops = &xorfs_aops; if(!(sb->s_root = d_alloc_root(xorfs_root_inode))) { iput( xorfs_root_inode ); return -ENOMEM; } // if return 0; } // xorfs_fill_super /** * super_operations */ static int xorfs_super_write_inode(struct inode *inode, struct writeback_control *wbc) { printk("XORFS: xorfs_super_write_inode (i_ino=%d) = %d\n", (int) inode->i_ino, (int) i_size_read(inode)); if(inode->i_ino == FILE_INODE_NUMBER) { file_size = i_size_read(inode); } return 0; } // xorfs_super_write_inode /** static void* xorfs_super_read_inode(struct super_block *sp, struct inode *inode) { inode->i_mapping->a_ops = &xorfs_aops; } // xorfs_super_read_inode */ /** * inode_operations */ static char filename[] = "hello.txt"; static int filename_len = sizeof(filename) - 1; static struct dentry* xorfs_inode_lookup(struct inode* parent_inode, struct dentry *dentry, struct nameidata *nameidata) { struct inode *file_inode; printk("XORFS: xorfs_inode_lookup\n"); if(parent_inode->i_ino != xorfs_root_inode->i_ino || dentry->d_name.len != filename_len || strncmp(dentry->d_name.name, filename, dentry->d_name.len)) { d_add(dentry, NULL); return NULL; } // if file_inode = xorfs_iget(parent_inode->i_sb, FILE_INODE_NUMBER); if(!file_inode) return ERR_PTR(-EACCES); file_inode->i_size = file_size; file_inode->i_mode = S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; file_inode->i_fop = &xorfs_fops; d_add(dentry, file_inode); return NULL; } // xorfs_inode_lookup static struct inode* xorfs_iget(struct super_block *sb, unsigned long ino) { struct inode *inode; int ret; printk("XORFS: xorfs_iget\n"); inode = iget_locked(sb, ino); if(!inode) return ERR_PTR(-ENOMEM); if(!(inode->i_state & I_NEW)) return inode; inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; inode->i_mapping->a_ops = &xorfs_aops; unlock_new_inode(inode); return inode; } // xorfs_iget /** * file_operations */ static int xorfs_file_open(struct inode *inode, struct file *file) { printk("XORFS: @xorfs_file_open max_readahead = %d (size = %d)\n", (int) file->f_ra.ra_pages, file_size); file->f_ra.ra_pages = 0; /* No read-ahead */ return generic_file_open(inode, file); } // xorfs_file_open static ssize_t xorfs_file_write(struct file *file, const char *buf, size_t maxlen, loff_t* offset) { int count; if(*offset > 0) { printk("XORFS: @xorfs_file_write Positive offset %d\n", *offset); return 0; } // if count = maxlen > sizeof(file_buf) ? sizeof(file_buf) : maxlen; //__generic_copy_from_user(file_buf, buf, count); copy_from_user(file_buf, buf, maxlen); printk("XORFS: xorfs_file_write called with maxlen=%d, offset=%d\n", maxlen, *offset); *offset += count; if(*offset > file_size) file_size = *offset; return count; } // xorfs_file_write static ssize_t xorfs_file_read(struct file *file, char *buf, size_t max, loff_t* offset){ int i; int buflen; if(*offset > 0) return 0; printk("XORFS: xorfs_file_read called [%d] [%d]\n", max, *offset); buflen = (file_size > max) ? max : file_size; copy_to_user(buf, file_buf, buflen); *offset += buflen; return buflen; } // xorfs_file_read static int xorfs_file_readdir(struct file *file, void *dirent, filldir_t filldir) { struct dentry *de = file->f_dentry; if(file->f_pos > 2) return 1; if(filldir(dirent, ".", 1, file->f_pos++, de->d_inode->i_ino, DT_DIR)) return 0; if(filldir(dirent, "..", 2, file->f_pos++, de->d_inode->i_ino, DT_DIR)) return 0; if(filldir(dirent, filename, filename_len, file->f_pos++, FILE_INODE_NUMBER, DT_REG)) return 0; return 1; } // xorfs_file_readdir static int xorfs_file_release(struct inode* ino, struct file *file) { struct dentry *dentry; dentry = file->f_dentry; return 0; } // xorfs_file_releasei /** * address_space_operations */ static int xorfs_readpage(struct file *file, struct page *page) { void *page_addr; printk("XORFS: xorfs_readpage called for page index=[%d]\n", (int) page->index); if(page->index > 0) { return -ENOSPC; } printk("XORFS: Page: [%s] [%s] [%s] [%s]\n", PageUptodate(page) ? "Uptodate" : "Not Uptodate", PageDirty(page) ? "Dirty" : "Not Dirty", PageWriteback(page) ? "PageWriteback Set" : "PageWriteback Cleared", PageLocked(page) ? "Locked" : "Unlocked"); SetPageUptodate(page); page_addr = kmap(page); if(page_addr) memcpy(page_addr, file_buf, PAGE_SIZE); if(PageLocked(page)) unlock_page(page); kunmap(page); return 0; } // xorfs_readpage static int xorfs_writepage(struct page *page, struct writeback_control *wbc) { void *page_addr = kmap(page); printk("[XORFS] xorfs_writepage, offset = %d\n", (int) page->index); printk("XORFS: WritePage: [%s] [%s] [%s] [%s]\n", PageUptodate(page) ? "Uptodate" : "Not Uptodate", PageDirty(page) ? "Dirty" : "Not Dirty", PageWriteback(page) ? "PageWriteback Set" : "PageWriteback Cleared", PageLocked(page) ? "Locked" : "Unlocked"); memcpy(file_buf, page_addr, PAGE_SIZE); ClearPageDirty(page); if(PageLocked(page)) unlock_page(page); kunmap(page); return 0; } // xorfs_writepage static int xorfs_write_begin(struct file *file, struct address_space *mapping, loff_t pos, unsigned len, unsigned flags, struct page **pagep, void **fsdata) { printk("XORFS: xorfs_write_begin\n"); return 0; } // xorfs_write_begin static int xorfs_write_end(struct file *file, struct address_space *mapping, loff_t pos, unsigned len, unsigned copied, struct page *page, void *fsdata) { struct inode *inode = page->mapping->host; void *page_addr = kmap(page); loff_t last_pos = pos + copied; printk("XORFS: xorfs_write_end: [%s] [%s] [%s] \n", PageUptodate(page) ? "Uptodate" : "Not Uptodate", PageDirty(page) ? "Dirty" : "Not Dirty", PageLocked(page) ? "Locked" : "Unlocked"); if(page->index == 0) { memcpy(file_buf, page_addr, PAGE_SIZE); ClearPageDirty(page); } // if SetPageUptodate(page); kunmap(page); if(last_pos > inode->i_size) { i_size_write(inode, last_pos); mark_inode_dirty(inode); } // if return 0; } // xorfs_write_end /* * register module */ static int __init xorfs_init_module(void) { int err; printk("XORFS: init_module\n"); err = register_filesystem( &xorfs ); return err; } // init_module static void __exit xorfs_cleanup_module(void) { unregister_filesystem(&xorfs); } // xorfs_cleanup_module module_init(xorfs_init_module); module_exit(xorfs_cleanup_module);
Makefile文件如下:
ifneq (${KERNELRELEASE},)
obj-m += xorfs.o
else
KERNEL_SOURCE :=/lib/modules/$(shell uname -r)/build
PWD :=$(shell pwd)
export EXTRA_CFLAGS := -std=gnu99
?
default:
??? $(MAKE) -C ${KERNEL_SOURCE} SUBDIRS=$(PWD) modules
clean:
??? rm *.o *.ko
endif
1、編譯時(shí),運(yùn)行如下命令:
# make
2、將編譯成功的模板安裝到文件系統(tǒng)時(shí),運(yùn)行如下命令:
# insmod xorfs.ko
3、在/mnt下創(chuàng)建掛載點(diǎn) xorfs,運(yùn)行命令:
# mkdir /mnt/xorfs
4、將xorfs文件系統(tǒng)裝載到/mnt/xorfs下,運(yùn)行命令:
# mount –t xorfs xorfs /mnt/xorfs
5、若要卸載/mnt/xorfs,運(yùn)行命令:
# umount /mnt/xorfs
6、注銷(xiāo)文件系統(tǒng)xorfs時(shí),(注銷(xiāo)前先卸裁)運(yùn)行命令:
# rmmod xorfs.ko
7、查看printk日志信息,運(yùn)行命令:
# cat /var/log/messages | tail
?? 裝載xorfs之后,運(yùn)行效果如下:
源程序打包下載地址:
http://download.csdn.net/detail/ture010love/4235247
?
參考:
http://www.ibm.com/developerworks/cn/linux/l-cn-vfs/
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
