一个简单文件系统的实现

 /*转载请注明作者
author goter
email prowlman@gmail.com
*/
花了将进两个月的时候阅读完内核文件系统,对于文件系统是如何运行的还是有点模糊,所以想通过写一个简单的文件系统来使自己对文

件系统有个深入的了解。经过拷贝抄袭ext2和minix文件系统后,写了一个简单的文件系统,我把这个简单的文件系统叫作GTFS,估计还

有很多BUG,欢迎大家修正这些BUG
GTFS不支持磁盘配额和acl。链接,读写,删除,新建,改名等都支持,有时候会有莫名其妙的错误,希望大家谅解

下面先附上代码和使用信息

使用信息
在fs/Makefile中添加一行

[Copy to clipboard] [ - ]
CODE:
obj-$(CONFIG_GT_FS)                += gt/

在fs/Kconfig中添加

[Copy to clipboard] [ - ]
CODE:
config GT_FS
        tristate "GOTER fs support"
        help
           GTFS is a simple Linux file system for hard disk.

在include/linux/Kbuild中添加一行

[Copy to clipboard] [ - ]
CODE:
unifdef-y += gt_fs.h

在include/linux/magic.h中添加一行

[Copy to clipboard] [ - ]
CODE:
#define GT_SUPER_MAGIC                0x0601

为了迎接六一儿童节,所以GTFS的魔数就设为0x0601了

然后将附件中的压缩包解压,可以看到有两个目录
一个目录是mkfs.gt,进入mkfs.gt目录,里边有4个文件,mkfs.gt为生成的二进制文件,可以使用gcc -o mkfs.gt mkfs.gt.c生成mkfs.gt

工具,这个工具用来创建GT文件系统,只支持mkfs.gt /dev/sdax(设备名)命令。
另一个目录是gtfs,进入gtfs目录,里边有8个文件,将gtfs/gt_fs.h移动到内核include/linux/下,然后在内核fs/ 下新建目录gt,将剩

余的7个文件移动到gt下

然后make menuconfig,到filesystem下选中gtfs,然后编译即可

GT-FS.tar.gz (19.69 KB)
我会在下面为大家分析GTFS代码的


因为GTFS只是用来学习而不是用于生产环境的,所以GTFS的设计很简单也很简陋。GTFS的主要数据结构和其他文件系统相似,主要的区别

是组织块的方式
下面是GTFS的主要数据结构
gt_inode(索引节点,在gt_fs.h中定义)

[Copy to clipboard] [ - ]
CODE:
struct gt_inode {
__le16 i_mode;
__le16 i_uid;
__le16 i_gid;
__le32 i_size;
__le32 i_atime;
__le32 i_ctime;
__le32 i_mtime;
__le32 i_dtime;
__le16 i_nlinks;
__le32 i_flags;
__le32 i_start_block;//索引节点开始块
__le32 i_end_block;//素引节点结束块
__le32 i_blocks;//索引节点占用的块数,另外作为索引节点是否是最后一个的flag
__le16 i_dev;
__le32 i_reserved;//为该索引节点预留的块数
__u8 i_nouse[10];
}

很简单是吧,i_blocks的用途有点特别,GTFS默认每个索引节点都会占用一个块,所以新建一个索引节点时,

i_start_block=i_end_block,但此时i_blocks和i_reserved为0,因为GTFS对磁盘的使用是顺序使用的,所以当新建一个索引节点时,上

一个索引节点就需要设置下预留块数,以便文件的增长。新建的索引节点的开始块等于结束块,但并不需要设置预留块数,因为它可以一

直使用到最后一个磁盘块(它没有下一个索引节点,我管这叫没有封顶),那么当操作系统打开一个文件时,如何判断这个文件对应的索

引节点是不是新建的呢,这就是i_blocks的作用,当i_blocks为0时,说明这个索引节点是新建的。当新建一个索引节点时,会设置上一

个索引节点的 i_blocks,

[Copy to clipboard] [ - ]
CODE:
i_blocks=i_end_blocks-i_start_blocks+1

所以,每个索引节点都至少占用一个块


gt_inode_info(内存中的i节点信息,在gt.h中定义)

[Copy to clipboard] [ - ]
CODE:
struct gt_inode_info{
__le32 i_start_block;
__le32 i_end_block;
__le32 i_blocks;
__le32 i_reserved;
__u32 i_flags;
__u32 i_dtime;
struct mutex truncate_mutex;
struct inode vfs_inode;
};

gt_super_block(超级块,在gt_fs.h中定义)

[Copy to clipboard] [ - ]
CODE:
struct gt_super_block {
__le32 s_inodes_count;
__le16 s_inode_size;
__le32 s_blocks_count;
__le32 s_free_blocks_count;
__le32 s_free_inodes_count;
__le32 s_first_data_block;
__le32 s_first_ino;
__le32 s_link_max;
__le32 s_log_block_size;
__le32 s_mtime;
__le32 s_wtime;
__le16 s_magic;
};

超级块数据结构没什么特别的,超级块的内容在挂载文件系统时从磁盘超级块读入

gt_sb_info(内存中的超级块,在gt_fs.h中定义)

[Copy to clipboard] [ - ]
CODE:
struct  gt_sb_info{
struct gt_super_block * s_gs;//指向GT文件系统的超级块
struct buffer_head * s_sbh;//指向超级块所在的缓冲区
};

gt_dir_entry(目录项,在gt_fs.h中定义)

[Copy to clipboard] [ - ]
CODE:
#define GT_NAME_LEN 60 //目录项名字长度
struct gt_dir_entry {
__le32 ino;
char name[GT_NAME_LEN];
};


文件系统的实现还需要对超级块的操作,对文件的操作,索引节点操作等等
我们就先从挂载一个文件系统开始
挂载文件系统时候,VFS会通过mount命令中的文件系统类型或超级块魔数来寻找file_system_type结构
下面就是GTFS的file_system_type结构(定义在super.c中)

[Copy to clipboard] [ - ]
CODE:
static struct file_system_type gt_fs_type ={
        .owner                =THIS_MODULE,
        .name                ="gt",//文件系统名称
        .get_sb                =gt_get_sb,//读取超级块方法
        .kill_sb        =kill_block_super,
        .fs_flags        =FS_REQUIRES_DEV,        
};

我们只需要完成gt_get_sb(定义在super.c中)方法即可

[Copy to clipboard] [ - ]
CODE:
static int gt_get_sb(struct file_system_type *fs_type,
int flags,const char *dev_name,void *data,struct vfsmount *mnt){
        return get_sb_bdev(fs_type,flags,dev_name,data,gt_fill_super,mnt);
}

从gt_get_sb函数可以看出,这个函数调用了get_sb_bdev函数,get_sb_bdev又调用了gt_fill_super函数
gt_fill_super函数才是正真用来读取超级块的方法

[Copy to clipboard] [ - ]
CODE:
static int gt_fill_super(struct super_block *sb,void *data,int silent){

        struct buffer_head *bh;
        struct gt_super_block *gs;
        struct gt_sb_info *sbi;
        struct inode *root;//设备的根目录
        
        unsigned long sb_block=1;//超级块的块号为1,第0块为启动块

        long ret=-EINVAL;
        
        int blocksize=BLOCK_SIZE;//BLOCK_SIZE为1024

        sbi=kzalloc(sizeof(struct gt_sb_info),GFP_KERNEL);
        if(!sbi)
                return -ENOMEM;

        if(!sb_set_blocksize(sb,BLOCK_SIZE))//设置VFS超级块的块大小
                goto out_bad_hblock;
        if(!(bh=sb_bread(sb,sb_block))){//将GTFS超级块所在的块读入内存
                printk("GT-fs:unable to read superblock/n");
                goto failed_sbi;
        }

        gs=(struct gt_super_block *)(bh->b_data);
        sbi->s_sbh=bh;//指向从磁盘读入的GTFS超级块所在的缓冲区
        sbi->s_gs=gs;//将内存中的GTFS超级块和从磁盘读入的GTFS超级块联系起来
        sb->s_fs_info=sbi;//将VFS超级块和GTFS的超级块联系起来
        sb->s_magic=gs->s_magic;//设置魔数

        if(sb->s_magic !=GT_SUPER_MAGIC)
                goto cantfind_gt;

        blocksize=GT_BLOCK_SIZE;

        sb->s_op=&gt_sops;
        
        root=gt_iget(sb,GT_ROOT_INO);//GT_ROOT_INO为1,读入文件系统的根目录
        if(IS_ERR(root)){
                ret=PTR_ERR(root);
                printk(KERN_ERR "GT-fs: can't find root inode/n");
                goto failed_mount;        
        }
        if (!S_ISDIR(root->i_mode) || !root->i_blocks || !root->i_size) {
                iput(root);
                printk(KERN_ERR "isdir?%d,root->i_blocks=%d,root->i_size=%d/n",S_ISDIR(root->i_mode) , root->i_blocks,

root->i_size);
                printk(KERN_ERR "GT-fs: corrupt root inode/n");
                goto failed_mount;
        }

        sb->s_root = d_alloc_root(root);//设置超级块的根目录
        if (!sb->s_root) {
                iput(root);
                printk(KERN_ERR "GT: get root inode failed/n");
                ret = -ENOMEM;
                goto failed_mount;
        }
        
        return 0;
cantfind_gt:
        printk("VFS: Can't find an gt filesystem on dev %s./nmagic on dev is %d and magic of GT is %d/n",sb->s_id,sb-

>s_magic,GT_SUPER_MAGIC);
failed_mount:
        brelse(bh);
out_bad_hblock:
        printk("GT-fs:blocksize too small for device/n");
failed_sbi:
        sb->s_fs_info=NULL;
        kfree(sbi);
        return ret;
}

这段函数主要是从磁盘读入文件系统的超级块,用来填充内存中的GTFS超级块,和VFS超级块
调用gt_iget函数读入指定文件系统的根目录。

文件系统在kernel中被当作一个模块实现
也有module_init和module_exit(定义在super.c)

[Copy to clipboard] [ - ]
CODE:
module_init(init_gt_fs)
module_exit(exit_gt_fs)

init_gt_fs(定义在super.c)

[Copy to clipboard] [ - ]
CODE:
static int __init init_gt_fs(void){
    int err=init_inodecache();
    if(err)
       return err;
    err=register_filesystem(&gt_fs_type);//向内核注册GT文件系统
    if(err)
       goto out;
    return 0;
out:
    destroy_inodecache();
    return err;
}

init_gt_fs中调用init_inodecache创建GTFS的内存索引节点缓冲池(我瞎猜的...对这个不了解..)

init_inodecache(定义在super.c中)

[Copy to clipboard] [ - ]
CODE:
static struct kmem_cache *gt_inode_cachep;
static int init_inodecache(void){
   gt_inode_cachep=kmem_cache_create("gt_inode_cache",sizeof(struct gt_inode_info),0,

(SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD),init_once);
   if(gt_inode_cachep==NULL)
      return -ENOMEM;
   return 0;
}

然后就是exit_gt_fs(定义在super.c中)

[Copy to clipboard] [ - ]
CODE:
static void __exit exit_gt_fs(void){
     unregister_filesystem(&gt_fs_type);//注销文件系统
     destroy_inodecache();//销毁缓冲池
}


接下来是超级块操作(定义在super.c中)

[Copy to clipboard] [ - ]
CODE:
static const struct super_operations gt_sops={
        .alloc_inode        =gt_alloc_inode,//分配一个GTFS的索引节点
        .destroy_inode        =gt_destroy_inode,//销毁索引节点
        .write_inode        =gt_write_inode,//写入索引节点
        .delete_inode        =gt_delete_inode,//删除索引节点
        .write_super    =gt_write_super,//将超级块写入磁盘
        .put_super        =gt_put_super,//释放超级块
        .statfs                =gt_statfs,//获取文件系统状态
        .write_inode        =gt_write_inode,//将索引节点写入磁盘
};

下面我们挨个介绍
gt_alloc_inode(定义在super.c中)

[Copy to clipboard] [ - ]
CODE:
static struct inode *gt_alloc_inode(struct super_block *sb){
        struct gt_inode_info *gi;

        gi=(struct gt_inode_info *)kmem_cache_alloc(gt_inode_cachep,GFP_KERNEL);//在缓冲池分配一个GTFS的内存索引节点
        if(!gi)
                return NULL;
        return &gi->vfs_inode;
}

函数gt_destroy_inode(定义在super.c中)

[Copy to clipboard] [ - ]
CODE:
static void gt_destroy_inode(struct inode *inode){
        kmem_cache_free(gt_inode_cachep,GT_I(inode));
}

从缓冲池中释放GTFS的内存中索引节点
GT_I是定义在gt.h中的一个inline函数,根据VFS索引节点返回GTFS的内存索引节点

[Copy to clipboard] [ - ]
CODE:
static inline struct gt_inode_info *GT_I(struct inode *inode){
          return container_of(inode,struct gt_inode_info,vfs_inode);
}

然后是gt_write_inode(定义在super.c中)

[Copy to clipboard] [ - ]
CODE:
static int gt_write_inode(struct inode *inode,int wait){
        brelse(gt_update_inode(inode));
        return 0;
}

可见,gt_write_inode函数调用gt_update_inode来将内存中的索引节点写入磁盘

gt_update_inode(定义在inode.c中)

[Copy to clipboard] [ - ]
CODE:
struct buffer_head *gt_update_inode(struct inode *inode){
               
        struct gt_inode_info *gi=GT_I(inode);
        struct super_block *sb=inode->i_sb;
        ino_t ino=inode->i_ino;
        uid_t uid=inode->i_uid;
        gid_t gid=inode->i_gid;
        struct buffer_head *bh;
        struct gt_inode *raw_inode =gt_raw_inode(sb,ino,&bh);//根据超级块和索引节点号从磁盘读入GTFS的磁盘索引节点

        if(!raw_inode)
                return NULL;
        /*更新*/
        raw_inode->i_mode=inode->i_mode;
        raw_inode->i_uid=uid;
        raw_inode->i_gid=gid;
        raw_inode->i_nlinks=inode->i_nlink;
        raw_inode->i_size=inode->i_size;
        raw_inode->i_atime=inode->i_atime.tv_sec;
        raw_inode->i_mtime=inode->i_mtime.tv_sec;
        raw_inode->i_ctime=inode->i_ctime.tv_sec;
        //raw_inode->i_dtime=inode->i_dtime.tv_sec;
       
        if(S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
                raw_inode->i_dev=old_encode_dev(inode->i_rdev);
        else{
                raw_inode->i_start_block=gi->i_start_block;
                raw_inode->i_end_block=gi->i_end_block;
                raw_inode->i_blocks=gi->i_blocks;
                raw_inode->i_reserved=gi->i_reserved;
                       
        }
        mark_buffer_dirty(bh);//将磁盘索引节点所在的缓冲块标记为脏
        return bh;
}

gt_update_inode 很简单,通过调用gt_raw_inode来读入GTFS的磁盘索引节点,然后根据GTFS内存索引节点去设置磁盘索引节点,最后将

磁盘索引节点所在的缓冲块标记为脏,等待机会写入磁盘。gt_raw_inode将在后面介绍,我们回到超级块操作来

接下来是gt_delete_inode(定义在super.c中)

[Copy to clipboard] [ - ]
CODE:
static void gt_delete_inode(struct inode *inode){
        truncate_inode_pages(&inode->i_data,0);
        GT_I(inode)->i_dtime=get_seconds();
        inode->i_size=0;
        gt_truncate(inode);//清空文件
        gt_free_inode(inode);//释放索引节点
}

让我们来看看gt_truncate(定义在inode.c中)

[Copy to clipboard] [ - ]
CODE:
void gt_truncate(struct inode *inode){
        if(!(S_ISREG(inode->i_mode)||S_ISDIR(inode->i_mode)||S_ISLNK(inode->i_mode)))
                return;
        struct gt_inode_info *gi=GT_I(inode);
        block_truncate_page(inode->i_mapping,inode->i_size,gt_get_block);

        gi->i_reserved+=gi->i_end_block-gi->i_start_block+1;//设置预留块数
        gi->i_end_block=gi->i_start_block;//清空
        inode->i_mtime=inode->i_ctime=CURRENT_TIME_SEC;
        mark_inode_dirty(inode);
}

这个函数主要是设置内存索引节点的块使用状况

还有gt_free_inode(定义在inode.c中)

[Copy to clipboard] [ - ]
CODE:
void gt_free_inode(struct inode *inode){
        struct super_block *sb=inode->i_sb;
        struct gt_super_block *gs=GT_SB(inode->i_sb)->s_gs;
        struct buffer_head *bh;
        unsigned long ino;
        ino=inode->i_ino;
        struct gt_inode *raw_inode=NULL;
        if(ino<1||ino>gs->s_inodes_count){
                printk("gt_free_inode: inode 0 or nonexistent inode/n");
                return;
        }
        raw_inode=gt_raw_inode(sb,ino,&bh);
        if(raw_inode){
                raw_inode->i_nlinks=0;//设置磁盘索引节点的连接数
                raw_inode->i_mode=0;
        }
        if(bh){
                mark_buffer_dirty(bh);
                brelse(bh);
        }
        clear_inode(inode);//调用VFS函数清理VFS索引节点
}

这个函数也很简单,只是读取磁盘索引节点,然后设置连接数和模式,然后标记磁盘索引节点所在的缓冲块为脏



接着写超级块操作
接下来就是gt_write_super(定义在super.c中)

[Copy to clipboard] [ - ]
CODE:
static int gt_write_super(struct super_block *sb){
        struct gt_super_block *gs;
        lock_kernel();
        gs=GT_SB(sb)->s_gs;
        gs->s_free_blocks_count=cpu_to_le32(gt_count_free_blocks(sb));
        gs->s_free_inodes_count=cpu_to_le32(gt_count_free_inodes(sb));
        gs->s_mtime=cpu_to_le32(get_seconds());
        gs->s_wtime = cpu_to_le32(get_seconds());
        mark_buffer_dirty(GT_SB(sb)->s_sbh);
        sync_dirty_buffer(GT_SB(sb)->s_sbh);
        sb->s_dirt=0;       
        unlock_kernel();
}

这个很简单很简单,简单到我都懒的讲了..
让我们来看看gt_count_free_blocks和gt_count_free_inodes
这俩函数都定义在inode.c中

[Copy to clipboard] [ - ]
CODE:
unsigned long gt_count_free_inodes(struct super_block *sb){
        struct buffer_head *bh;
        struct gt_inode *gt;
        char *p;
         
        unsigned long block=2; //索引节点表所在块
        unsigned long count=0;//使用了的索引节点数
        //然后遍历索引节点表
        while(bh=sb_bread(sb,block)){
                p=bh->b_data;
                while(p<=(bh->b_data+GT_BLOCK_SIZE-GT_INODE_SIZE)){
                        gt=(struct gt_inode *)p;
                        if(gt->i_nlinks)
                                count++;//已经使用的索引节点数加一
                        p+=GT_INODE_SIZE;
                }
                brelse(bh);
                if(block>GT_INODE_BLOCK_COUNT(sb))//如果到了索引节点表结尾则跳出
                        break;
                block++;       
        }
       
        return GT_SB(sb)->s_gs->s_inodes_count-count;//返回未使用的索引节点数
}



[Copy to clipboard] [ - ]
CODE:
unsigned long gt_count_free_blocks(struct super_block *sb){

        struct gt_super_block *gs;
        char *p;
        int block=2;
        gs=GT_SB(sb)->s_gs;
        unsigned long used=0;//已经使用的块数
        struct buffer_head *bh;
        struct gt_inode * gt;
       //遍历索引节点表,已经使用的块数其实就等于最后一个索引节点的i_end_block
        while(bh=sb_bread(sb,block)){
                p=bh->b_data;
                while(p<=(bh->b_data+GT_BLOCK_SIZE-GT_INODE_SIZE)){
                        gt=(struct gt_inode *)p;
                        if(!gt->i_blocks)
                                used=gt->i_end_block;
                               
                }
                brelse(bh);
        }
        return GT_BLOCKS(sb)-used;
}


gt_put_super(定义在super.c中)

[Copy to clipboard] [ - ]
CODE:
static void gt_put_super(struct super_block *sb){
        struct gt_sb_info *sbi=GT_SB(sb);
        brelse(sbi->s_sbh);
        sb->s_fs_info=NULL;
        kfree(sbi);
}

这个函数释放掉磁盘超级块所在的缓冲块,释放掉内存超级块

gt_statfs(定义在super.c中)

[Copy to clipboard] [ - ]
CODE:
static int gt_statfs(struct dentry *dentry,struct kstatfs *buf){
        struct gt_sb_info * sbi=GT_SB(dentry->d_sb);
        struct gt_super_block *gs=sbi->s_gs;
        buf->f_type=dentry->d_sb->s_magic;
        buf->f_bsize=dentry->d_sb->s_blocksize;
        buf->f_blocks=(gs->s_blocks_count-gs->s_first_data_block);
        buf->f_bfree=gt_count_free_blocks(sbi);
        buf->f_bavail=buf->f_bfree;
        buf->f_ffree=gt_count_free_inodes(sbi);
        buf->f_namelen=GT_NAME_LEN;
        return 0;
}

这个函数获取文件系统状态

到这里超级块的操作就完成了,接下来是普通索引节点操作,只需要一个清空函数,其他的调用vfs默认的函数

[Copy to clipboard] [ - ]
CODE:
const struct inode_operations gt_file_inode_operations ={
        .truncate        =gt_truncate,
};

普通文件操作(在file.c中定义),只需要实现一个gt_sync_file

[Copy to clipboard] [ - ]
CODE:
const struct file_operations gt_file_operations ={
        .llseek                =generic_file_llseek,
        .read                =do_sync_read,
        .write                =do_sync_write,
        .aio_read        =generic_file_aio_read,
        .aio_write        =generic_file_aio_write,
        .mmap                =generic_file_mmap,
        .open                =generic_file_open,
        .fsync                =gt_sync_file,
};

来看看gt_sync_file(在file.c中定义)

[Copy to clipboard] [ - ]
CODE:
int gt_sync_file(struct file *file,struct dentry *dentry,int datasync){
        struct inode *inode =dentry->d_inode;
        int err,ret;
        ret=sync_mapping_buffers(inode->i_mapping);
        if(!(inode->i_state&I_DIRTY))
                return ret;
        if(datasync && !(inode->i_state&I_DIRTY_DATASYNC))
                return ret;
        err=gt_sync_inode(inode);
        if(ret==0)
                ret=err;
        return ret;
}

函数调用gt_sync_inode(在inode.c中定义)

[Copy to clipboard] [ - ]
CODE:
int gt_sync_inode(struct inode *inode){
        int ret=0;
        struct buffer_head *bh;
        bh=gt_update_inode(inode);//获取到磁盘索引节点所在的缓冲区
        if(bh && buffer_dirty(bh)){//如果为脏,则同步
                sync_dirty_buffer(bh);
                if(buffer_req(bh)&&!buffer_uptodate(bh)){
                        printk("IO error syncing gt inode/n");
                        ret=-1;
                }
        }else if(!bh)
                ret=-1;
        brelse(bh);
        return ret;
}



一个简单文件系统的实现


然后就是目录操作(在dir.c中定义)

[Copy to clipboard] [ - ]
CODE:
const struct file_operations gt_dir_operations ={
        .llseek        =generic_file_llseek,
        .read                =generic_read_dir,
        .readdir        =gt_readdir,
        .fsync        =gt_sync_file,
};

gt_readdir(在dir.c中定义)
linux文件系统对文件的读写是以页为单位的。

[Copy to clipboard] [ - ]
CODE:
static int gt_readdir(struct file *filp,void *dirent,filldir_t filldir){
        loff_t pos=filp->f_pos;//目前读到的位置,文件内偏移
       
        struct inode *inode=filp->f_path.dentry->d_inode;//目录文件的inode
       
        unsigned int offset=pos & ~PAGE_CACHE_MASK;
        unsigned long n=pos >> PAGE_CACHE_SHIFT;//目前读到第几页
        unsigned long npages=gt_dir_pages(inode);//该文件在内存中占用的页数
        unsigned chunk_size=sizeof(struct gt_dir_entry);
        lock_kernel();
        pos=(pos+chunk_size-1)&~(chunk_size-1);//将目前读到的位置调整到目录项开始
        if(pos>=inode->i_size)
                goto done;       
        for(;n<npages;n++,offset=0){//遍历文件所占用的页
                char *kaddr,*limit;
                char *p;
       
                struct page *page=gt_get_page(inode,n);//获取到第n页
       
                if(IS_ERR(page)){
                        continue;
                        printk("page is error/n");
                }
       
                kaddr=(char *)page_address(page);//第n页在内存中的地址
       
                p=(kaddr+offset);//指向当前该读入的目录项的地址
       
                limit=kaddr+gt_last_byte(inode,n)-chunk_size;//边界
                for(;p<=limit;p=gt_next_entry(p)){
                        struct gt_dir_entry * de=(struct gt_dir_entry*)p;
                          
                        if(de->ino){//目录项有对应的索引节点号
                                       
                                offset=p -kaddr;//在页内的偏移
                                unsigned name_len=strnlen(de->name,GT_NAME_LEN);
                                unsigned char d_type=DT_UNKNOWN;
                                int over=filldir(dirent,de->name,name_len,(n<< PAGE_CACHE_SHIFT)|offset,le32_to_cpu(de-

>ino),d_type);//调用VFS函数填充 dirent结构,有兴趣的朋友可以自己去研究
                                if(over){
                                        gt_put_page(page);//如果错误就释放该页,跳到done标号处
                                        goto done;
                                }
                        }                       
                }
                gt_put_page(page);
        }
done:
        filp->f_pos=(n<<PAGE_CACHE_SHIFT)|offset;//调整当前文件内偏移
        unlock_kernel();
        return 0;       
}

这段函数调用了几个新函数,主要是使用作为参数传递进来的filldir函数来完成gt_readdir
下面来看看这段函数中调用的新函数
gt_dir_pages(在dir.c中定义)

[Copy to clipboard] [ - ]
CODE:
static inline unsigned long gt_dir_pages(struct inode *inode){
        return (inode->i_size+PAGE_CACHE_SIZE-1)>>PAGE_CACHE_SHIFT;
}

很简单,就不解释了
gt_get_page(在dir.c中定义)

[Copy to clipboard] [ - ]
CODE:
static struct page *gt_get_page(struct inode *dir,unsigned long n){
        struct address_space *mapping=dir->i_mapping;
        struct page *page=read_mapping_page(mapping,n,NULL);
        if(!IS_ERR(page)){
                kmap(page);
                if(!PageUptodate(page))
                        goto fail;
        }
        return page;
fail:
        gt_put_page(page);
        return ERR_PTR(-EIO);
}

gt_get_page主要通过调用read_mapping_page实现,另外还加了点错误判断等

gt_last_byte(在dir.c中定义)

[Copy to clipboard] [ - ]
CODE:
static unsigned gt_last_byte(struct inode *inode, unsigned long page_nr){
        unsigned last_byte = inode->i_size;

        last_byte -= page_nr << PAGE_CACHE_SHIFT;
        if (last_byte > PAGE_CACHE_SIZE)
                last_byte = PAGE_CACHE_SIZE;
        return last_byte;
}

gt_last_byte函数用来返回文件在第n个页中的偏移,在前n-1个页中偏移都是PAGE_CACHE_SIZE

gt_next_entry(在dir.c中定义)

[Copy to clipboard] [ - ]
CODE:
static inline void *gt_next_entry(void *de){
        return (void *)((char *)de+sizeof(struct gt_dir_entry));
}

这个函数也很简单,主用用来遍历目录项

gt_sync_file在前边已经分析完
这样目录操作也分析完了,下边是地址空间操作gt_aops




因为地址空间操作的操作对象是内存,与磁盘的组织方式无关,所以这个我们可以直接使用VFS的地址空间操作,这样就简单多了
gt_aops(在inode.c中定义)

[Copy to clipboard] [ - ]
CODE:
const struct address_space_operations gt_aops ={
        .readpage                =gt_readpage,
        .writepage                =gt_writepage,
        .sync_page                =block_sync_page,
        .write_begin                =gt_write_begin,
        .write_end                =generic_write_end,
        .bmap                        =gt_bmap,
};

我就直接贴代码了,这部分对于我们实现一个文件系统来说很简单,如果你想继续深入下去,可以跟踪这些函数读下去
这些函数都是在inode.c中定义实现

[Copy to clipboard] [ - ]
CODE:
static int gt_writepage(struct page *page,struct writeback_control *wbc){

        return block_write_full_page(page,gt_get_block,wbc);
}

static int gt_readpage(struct file *file,struct page *page){
        return block_read_full_page(page,gt_get_block);
}

int __gt_write_begin(struct file *file,struct address_space *mapping,loff_t pos,unsigned len,unsigned flags,struct page

*pagep,void **fsdata){
        return block_write_begin(file,mapping,pos,len,flags,pagep,fsdata,gt_get_block);

}

static int gt_write_begin(struct file *file,struct address_space *mapping,
loff_t pos,unsigned len,unsigned flags,struct page **pagep,void **fsdata){
        *pagep=NULL;
        return __gt_write_begin(file,mapping,pos,len,flags,pagep,fsdata);
}

static sector_t gt_bmap(struct address_space *mapping,sector_t block){
        return generic_block_bmap(mapping,block,gt_get_block);
}

地址空间操作就是这样,接下来是对目录索引节点的操作


已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页