用戶視角下的文件系統
??? “一切皆是文件”,是UNIX和Linux的基本哲學之一。Linux對于文件I/O操作,實現了POSIX.1和Single UNIX Specification中的接口,包括open()、read()、write()、lseek()和close()等方法。正是由于Linux所實現的虛擬文件系統對具體文件系統進行了抽象,使得Linux可以方便地實現文件I/O操作接口。用戶視角下的文件系統,就是一組系統調用接口,其與VFS的關系如下:
??? 每個進程在用戶空間內都有一張file description table,用于描述已打開的文件。當open()成功返回時,將返回文件描述符(file description),被插入到file description table中。
??? 如下圖所示,當用戶進程調用write()方法讀取文件時,將調用VFS的sys_write()方法,而在sys_write()方法中調用文件系統接口的具體方法進行硬盤讀取。在Linux2.6.25以后,sys_write()為vfs_write()所替代。
??? vfs_write()源代碼如下:
??? 由此可見,write的調用過程為:→write()→vfs_write()→file->f_op->write(),由文件系統提供的VFS API進行實際的存取操作。
硬盤視角下的文件系統
??? Linux在硬盤上的文件系統與邏輯上的文件系統VFS完全不同。UFS(UNIX File System)基于Berkeley fast file system,如下:
??? UFS由許多分區構成,可以允許分區之間采取不同的文件系統,但同一個分區之內必須為同一文件系統。上圖 啟動塊 (Boot Block)大小確定,為1KB,由PC標準規定,用來存儲磁盤分區信息和啟動信息,任何文件系統都不能使用啟動塊。UFS文件系統將整個分區劃分成超級塊(Super Block,除塊組0之外的Super Block都為備份)、塊描述符表、i-node位圖、塊位圖、i-node表、data數據塊。
??? 超級塊 包含了關于該硬盤或分區文件系統的整體信息,如文件系統大小等。 索引結點 ,包含了針對某一具體文件幾乎的全部信息,如文件存取權限、所有者、大小、建立時間以及對應的目錄塊和數據塊等。 數據塊 是真正存儲文件內容的位置,但索引結點中不包括文件名,文件名存于目錄塊。 目錄塊 里包含文件名以及文件索引結點編號。
??? 上圖中,位于數據塊中存儲目錄數據的directory entry均指向同一個i-node,而i-node中包括三個data block。
內核虛擬文件系統VFS
內核文件系統主要的四個數據結構為:
??? superblock,代表一個具體的已掛載的文件系統;
??? inode,代表一個具體的文件;
??? dentry,代表一個目錄項,如/home/icanth,home和icanth都是一個目錄項;
??? file,代表一個進程已經打開的文件。
圖 super_block、file、dentry和inode的關系
??? 每個file結構體都指向一個file_operations結構體,這個結構體的成員都是函數指針,指向實現各種文件操作的內核函數。比如在用戶程序中read一個文件描述符,read通過系統調用進入內核,然后找到這個文件描述符所指向的file結構體,找到file結構體所指向的file_operations結構體,調用它的read成員所指向的內核函數以完成用戶請求。在用戶程序中調用lseek、read、write、ioctl、open等函數,最終都由內核調用file_operations的各成員所指向的內核函數完成用戶請求。file_operations結構體中的release成員用于完成用戶程序的close請求,之所以叫release而不叫close是因為它不一定真的關閉文件,而是減少引用計數,只有引用計數減到0才關閉文件。對于同一個文件系統上打開的常規文件來說,read、write等文件操作的步驟和方法應該是一樣的,調用的函數應該是相同的,所以圖中的三個打開文件的file結構體指向同一個file_operations結構體。如果打開一個字符設備文件,那么它的read、write操作肯定和常規文件不一樣,不是讀寫磁盤的數據塊而是讀寫硬件設備,所以file結構體應該指向不同的file_operations結構體,其中的各種文件操作函數由該設備的驅動程序實現。
??? 每個file結構體都有一個指向dentry結構體的指針,“dentry”是directory entry(目錄項)的縮寫。我們傳給open、stat等函數的參數的是一個路徑,例如/home/akaedu/a,需要根據路徑找到文件的inode。為了減少讀盤次數,內核緩存了目錄的樹狀結構,稱為dentry cache,其中每個節點是一個dentry結構體,只要沿著路徑各部分的dentry搜索即可,從根目錄/找到home目錄,然后找到akaedu目錄,然后找到文件a。dentry cache只保存最近訪問過的目錄項,如果要找的目錄項在cache中沒有,就要從磁盤讀到內存中。
??? 每個dentry結構體都有一個指針指向inode結構體。inode結構體保存著從磁盤inode讀上來的信息。在上圖的例子中,有兩個dentry,分別表示/home/akaedu/a和/home/akaedu/b,它們都指向同一個inode,說明這兩個文件互為硬鏈接。inode結構體中保存著從磁盤分區的inode讀上來信息,例如所有者、文件大小、文件類型和權限位等。每個inode結構體都有一個指向inode_operations結構體的指針,后者也是一組函數指針指向一些完成文件目錄操作的內核函數。和file_operations不同,inode_operations所指向的不是針對某一個文件進行操作的函數,而是影響文件和目錄布局的函數,例如添加刪除文件和目錄、跟蹤符號鏈接等等,屬于同一文件系統的各inode結構體可以指向同一個inode_operations結構體。
??? inode結構體有一個指向super_block結構體的指針。super_block結構體保存著從磁盤分區的超級塊讀取的信息,例如文件系統類型、塊大小等。super_block結構體的s_root成員是一個指向dentry的指針,表示這個文件系統的根目錄被mount到哪里,在上圖的例子中這個分區被mount到/home目錄下。
超級塊對象superblock
??? superblock是在<linux/fs.h>在下定義的結構體super_block.
struct super_block { //超級塊數據結構
??????? struct list_head s_list;??????????????? /*指向超級塊鏈表的指針*/
??????? ……
??????? struct file_system_type? *s_type;?????? /*文件系統類型*/
?????? struct super_operations? *s_op;???????? /*超級塊方法*/
??????? ……
struct list_head s_instances; /* 該類型文件系統*/
??????? ……
};
?
struct super_operations { //超級塊方法
??????? ……
??????? //該函數在給定的超級塊下創建并初始化一個新的索引節點對象
??????? struct inode *(*alloc_inode)(struct super_block *sb);
?????? ……
??????? //該函數從磁盤上讀取索引節點,并動態填充內存中對應的索引節點對象的剩余部分
void (*read_inode) (struct inode *);
?????? ……
};
索引結點對象inode
??? 索引節點對象存儲了文件的相關信息,代表了存儲設備上的一個實際的物理文件。當一個 文件首次被訪問時,內核會在內存中組裝相應的索引節點對象,以便向內核提供對一個文件進行操 作時所必需的全部信息;這些信息一部分存儲在磁盤特定位置,另外一部分是在加載時動態填充的。
struct inode {//索引節點結構
????? ……
????? struct inode_operations? *i_op;???? /*索引節點操作表*/
???? struct file_operations?? *i_fop;???? /*該索引節點對應文件的文件操作集*/
???? struct super_block?????? *i_sb;???? /*相關的超級塊*/
???? ……
};
?
struct inode_operations { //索引節點方法
???? ……
???? //該函數為dentry對象所對應的文件創建一個新的索引節點,主要是由open()系統調用來調用
???? int (*create) (struct inode *,struct dentry *,int, struct nameidata *);
?
???? //在特定目錄中尋找dentry對象所對應的索引節點
???? struct dentry * (*lookup) (struct inode *,struct dentry *, struct nameidata *);
???? ……
};
目錄項對象 dentry
??? 引入目錄項的概念主要是出于方便查找文件的目的。一個路徑的各個組成部分,不管是目錄還是 普通的文件,都是一個目錄項對象。如,在路徑/home/source/test.c中,目錄 /, home, source和文件 test.c都對應一個目錄項對象。不同于前面的兩個對象,目錄項對象沒有對應的磁盤數據結構,VFS在遍 歷路徑名的過程中現場將它們逐個地解析成目錄項對象。
struct dentry {//目錄項結構
???? ……
???? struct inode *d_inode;?????????? /*相關的索引節點*/
??? struct dentry *d_parent;???????? /*父目錄的目錄項對象*/
??? struct qstr d_name;????????????? /*目錄項的名字*/
??? ……
???? struct list_head d_subdirs;????? /*子目錄*/
??? ……
???? struct dentry_operations *d_op;? /*目錄項操作表*/
??? struct super_block *d_sb;??????? /*文件超級塊*/
??? ……
};
?
struct dentry_operations {
??? //判斷目錄項是否有效;
??? int (*d_revalidate)(struct dentry *, struct nameidata *);
??? //為目錄項生成散列值;
??? int (*d_hash) (struct dentry *, struct qstr *);
??? ……
};
文件 對象 file
struct file {
??? ……
???? struct list_head??????? f_list;??????? /*文件對象鏈表*/
??? struct dentry????????? *f_dentry;?????? /*相關目錄項對象*/
??? struct vfsmount??????? *f_vfsmnt;?????? /*相關的安裝文件系統*/
??? struct file_operations *f_op;?????????? /*文件操作表*/
??? ……
};
?
struct file_operations {
??? ……
??? //文件讀操作
??? ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
??? ……
??? //文件寫操作
??? ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
??? ……
??? int (*readdir) (struct file *, void *, filldir_t);
??? ……
??? //文件打開操作
??? int (*open) (struct inode *, struct file *);
??? ……
};
文件系統對象
??? 根據文件系統所在的物理介質和數據在物理介質上的組織方式來區分不同的文件系統類型的。 file_system_type結構用于描述具體的文件系統的類型信息。被Linux支持的文件系統,都有且僅有一 個file_system_type結構而不管它有零個或多個實例被安裝到系統中。而與此對應的是每當一個文件系統被實際安裝,就有一個vfsmount結構體被創建,這個結構體對應一個安裝點。
struct file_system_type {
??????? const char *name;??????????????? /*文件系統的名字*/
??????? struct subsystem subsys;???????? /*sysfs子系統對象*/
??????? int fs_flags;??????????????????? /*文件系統類型標志*/
?
??????? /*在文件系統被安裝時,從磁盤中讀取超級塊,在內存中組裝超級塊對象*/
??????? struct super_block *(*get_sb) (struct file_system_type*,
??????????????????????????????????????? int, const char*, void *);
??????????
??????? void (*kill_sb) (struct super_block *);? /*終止訪問超級塊*/???????????
??????? struct module *owner;??????????????????? /*文件系統模塊*/
??????? struct file_system_type * next;????????? /*鏈表中的下一個文件系統類型*/
??????? struct list_head fs_supers;????????????? /*具有同一種文件系統類型的超級塊對象鏈表*/
};
?
struct vfsmount
{
??????? struct list_head mnt_hash;?????????????? /*散列表*/
??????? struct vfsmount *mnt_parent;???????????? /*父文件系統*/
??????? struct dentry *mnt_mountpoint;?????????? /*安裝點的目錄項對象*/
??????? struct dentry *mnt_root;???????????????? /*該文件系統的根目錄項對象*/
??????? struct super_block *mnt_sb;????????????? /*該文件系統的超級塊*/
??????? struct list_head mnt_mounts;???????????? /*子文件系統鏈表*/
??????? struct list_head mnt_child;????????????? /*子文件系統鏈表*/
??????? atomic_t mnt_count;????????????????????? /*使用計數*/
??????? int mnt_flags;?????????????????????????? /*安裝標志*/
??????? char *mnt_devname;?????????????????????? /*設備文件名*/
??????? struct list_head mnt_list;?????????????? /*描述符鏈表*/
??????? struct list_head mnt_fslink;???????????? /*具體文件系統的到期列表*/
??????? struct namespace *mnt_namespace;???????? /*相關的名字空間*/
};
打開文件的流程
??? 由于sys_open()的代碼量大,函數調用關系復雜,以下主要是對該函數做整體的解析;而對其中的一些關鍵點,則列出其關鍵代碼。
a. 從sys_open()的函數調用關系圖可以看到 ,sys_open()在做了一些簡單的參數檢驗后,就把接力棒傳給do_sys_open():
??? 1)首先,get_unused_fd()得到一個可用的文件描述符;通過該函數,可知文件描述符實質是進程打開文件列表中對應某個文件對象的索引值;
??? 2)接著,do_filp_open()打開文件,返回一個file對象,代表由該進程打開的一個文件;進程通過這樣的一個數據結構對物理文件進行讀寫操作。
??? 3)最后,fd_install()建立文件描述符與file對象的聯系,以后進程對文件的讀寫都是通過操縱該文件描述符而進行。
b. do_filp_open()用于打開文件 ,返回一個file對象;而打開之前需要先找到該文件:
??? 1)open_namei()用于根據文件路徑名查找文件,借助一個持有路徑信息的數據結構nameidata而進行;
??? 2)查找結束后將填充有路徑信息的nameidata返回給接下來的函數nameidata_to_filp()從而得到最終的file對象;當達到目的后,nameidata這個數據結構將會馬上被釋放。
c.open_namei()用于查找一個文件:
??? 1)path_lookup_open()實現文件的查找功能;要打開的文件若不存在,還需要有一個新建的過程,則調用path_lookup_create(),后者和前者封裝的是同一個實際的路徑查找函數,只是參數不一樣,使它們在處理細節上有所偏差;
??? 2)當是以新建文件的方式打開文件時,即設置了O_CREAT標識時需要創建一個新的索引節點,代表創建一個文件。在vfs_create()里的一句核心語句dir->i_op->create(dir, dentry, mode, nd)可知它調用了具體的文件系統所提供的創建索引節點的方法。注意:這邊的索引節點的概念,還只是位于內存之中,它和磁盤上的物理的索引節點的關系就像位于內存中和位于磁盤中的文件一樣。此時新建的索引節點還不能完全標志一個物理文件的成功創建,只有當把索引節點回寫到磁盤上才是一個物理文件的真正創建。想想我們以新建的方式打開一個文件,對其讀寫但最終沒有保存而關閉,則位于內存中的索引節點會經歷從新建到消失的過程,而磁盤卻始終不知道有人曾經想過創建一個文件,這是因為索引節點沒有回寫的緣故。
??? 3)path_to_nameidata()填充nameidata數據結構;
??? 4)may_open()檢查是否可以打開該文件;一些文件如鏈接文件和只有寫權限的目錄是不能被打開的,先檢查nd->dentry->inode所指的文件是否是這一類文件,是的話則錯誤返回。還有一些文件是不能以TRUNC的方式打開的,若nd->dentry->inode所指的文件屬于這一類,則顯式地關閉TRUNC標志位。接著如果有以TRUNC方式打開文件的,則更新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
??? 查找路徑的過程定義在link_path_work中,主要傳入nameidata對象
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; }
一個簡單的文件系統實現——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、編譯時,運行如下命令:
# make
2、將編譯成功的模板安裝到文件系統時,運行如下命令:
# insmod xorfs.ko
3、在/mnt下創建掛載點 xorfs,運行命令:
# mkdir /mnt/xorfs
4、將xorfs文件系統裝載到/mnt/xorfs下,運行命令:
# mount –t xorfs xorfs /mnt/xorfs
5、若要卸載/mnt/xorfs,運行命令:
# umount /mnt/xorfs
6、注銷文件系統xorfs時,(注銷前先卸裁)運行命令:
# rmmod xorfs.ko
7、查看printk日志信息,運行命令:
# cat /var/log/messages | tail
?? 裝載xorfs之后,運行效果如下:
源程序打包下載地址:
http://download.csdn.net/detail/ture010love/4235247
?
參考:
http://www.ibm.com/developerworks/cn/linux/l-cn-vfs/
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

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