用户视角下的文件系统
“一切皆是文件”,是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
参考: