Chinaunix

标题: 一个简单文件系统的实现 [打印本页]

作者: goter    时间: 2009-05-23 14:43
标题: 一个简单文件系统的实现
/*转载请注明作者
author goter
email prowlman@gmail.com
*/
花了将进两个月的时候阅读完内核文件系统,对于文件系统是如何运行的还是有点模糊,所以想通过写一个简单的文件系统来使自己对文件系统有个深入的了解。经过拷贝抄袭ext2和minix文件系统后,写了一个简单的文件系统,我把这个简单的文件系统叫作GTFS,估计还有很多BUG,欢迎大家修正这些BUG
GTFS不支持磁盘配额和acl。链接,读写,删除,新建,改名等都支持,有时候会有莫名其妙的错误,希望大家谅解

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

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

  1. obj-$(CONFIG_GT_FS)                += gt/
复制代码

在fs/Kconfig中添加

  1. config GT_FS
  2.         tristate "GOTER fs support"
  3.         help
  4.            GTFS is a simple Linux file system for hard disk.
复制代码

在include/linux/Kbuild中添加一行

  1. unifdef-y += gt_fs.h
复制代码

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

  1. #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, 下载次数: 1809)
我会在下面为大家分析GTFS代码的

[ 本帖最后由 goter 于 2009-5-23 20:17 编辑 ]
作者: goter    时间: 2009-05-23 14:44
因为GTFS只是用来学习而不是用于生产环境的,所以GTFS的设计很简单也很简陋。GTFS的主要数据结构和其他文件系统相似,主要的区别是组织块的方式
下面是GTFS的主要数据结构
gt_inode(索引节点,在gt_fs.h中定义)
  1. struct gt_inode {
  2. __le16 i_mode;
  3. __le16 i_uid;
  4. __le16 i_gid;
  5. __le32 i_size;
  6. __le32 i_atime;
  7. __le32 i_ctime;
  8. __le32 i_mtime;
  9. __le32 i_dtime;
  10. __le16 i_nlinks;
  11. __le32 i_flags;
  12. __le32 i_start_block;//索引节点开始块
  13. __le32 i_end_block;//素引节点结束块
  14. __le32 i_blocks;//索引节点占用的块数,另外作为索引节点是否是最后一个的flag
  15. __le16 i_dev;
  16. __le32 i_reserved;//为该索引节点预留的块数
  17. __u8 i_nouse[10];
  18. }
复制代码
很简单是吧,i_blocks的用途有点特别,GTFS默认每个索引节点都会占用一个块,所以新建一个索引节点时,i_start_block=i_end_block,但此时i_blocks和i_reserved为0,因为GTFS对磁盘的使用是顺序使用的,所以当新建一个索引节点时,上一个索引节点就需要设置下预留块数,以便文件的增长。新建的索引节点的开始块等于结束块,但并不需要设置预留块数,因为它可以一直使用到最后一个磁盘块(它没有下一个索引节点,我管这叫没有封顶),那么当操作系统打开一个文件时,如何判断这个文件对应的索引节点是不是新建的呢,这就是i_blocks的作用,当i_blocks为0时,说明这个索引节点是新建的。当新建一个索引节点时,会设置上一个索引节点的i_blocks,
  1. i_blocks=i_end_blocks-i_start_blocks+1
复制代码
所以,每个索引节点都至少占用一个块


gt_inode_info(内存中的i节点信息,在gt.h中定义)
  1. struct gt_inode_info{
  2. __le32 i_start_block;
  3. __le32 i_end_block;
  4. __le32 i_blocks;
  5. __le32 i_reserved;
  6. __u32 i_flags;
  7. __u32 i_dtime;
  8. struct mutex truncate_mutex;
  9. struct inode vfs_inode;
  10. };
复制代码
gt_super_block(超级块,在gt_fs.h中定义)
  1. struct gt_super_block {
  2. __le32 s_inodes_count;
  3. __le16 s_inode_size;
  4. __le32 s_blocks_count;
  5. __le32 s_free_blocks_count;
  6. __le32 s_free_inodes_count;
  7. __le32 s_first_data_block;
  8. __le32 s_first_ino;
  9. __le32 s_link_max;
  10. __le32 s_log_block_size;
  11. __le32 s_mtime;
  12. __le32 s_wtime;
  13. __le16 s_magic;
  14. };
复制代码
超级块数据结构没什么特别的,超级块的内容在挂载文件系统时从磁盘超级块读入

gt_sb_info(内存中的超级块,在gt_fs.h中定义)
  1. struct  gt_sb_info{
  2. struct gt_super_block * s_gs;//指向GT文件系统的超级块
  3. struct buffer_head * s_sbh;//指向超级块所在的缓冲区
  4. };
复制代码
gt_dir_entry(目录项,在gt_fs.h中定义)
  1. #define GT_NAME_LEN 60 //目录项名字长度
  2. struct gt_dir_entry {
  3. __le32 ino;
  4. char name[GT_NAME_LEN];
  5. };
复制代码

作者: goter    时间: 2009-05-23 14:45
文件系统的实现还需要对超级块的操作,对文件的操作,索引节点操作等等
我们就先从挂载一个文件系统开始
挂载文件系统时候,VFS会通过mount命令中的文件系统类型或超级块魔数来寻找file_system_type结构
下面就是GTFS的file_system_type结构(定义在super.c中)
  1. static struct file_system_type gt_fs_type ={
  2.         .owner                =THIS_MODULE,
  3.         .name                ="gt",//文件系统名称
  4.         .get_sb                =gt_get_sb,//读取超级块方法
  5.         .kill_sb        =kill_block_super,
  6.         .fs_flags        =FS_REQUIRES_DEV,        
  7. };
复制代码
我们只需要完成gt_get_sb(定义在super.c中)方法即可
  1. static int gt_get_sb(struct file_system_type *fs_type,
  2. int flags,const char *dev_name,void *data,struct vfsmount *mnt){
  3.         return get_sb_bdev(fs_type,flags,dev_name,data,gt_fill_super,mnt);
  4. }
复制代码
从gt_get_sb函数可以看出,这个函数调用了get_sb_bdev函数,get_sb_bdev又调用了gt_fill_super函数
gt_fill_super函数才是正真用来读取超级块的方法
  1. static int gt_fill_super(struct super_block *sb,void *data,int silent){

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

  8.         long ret=-EINVAL;
  9.         
  10.         int blocksize=BLOCK_SIZE;//BLOCK_SIZE为1024

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

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

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

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

  27.         blocksize=GT_BLOCK_SIZE;

  28.         sb->s_op=&gt_sops;
  29.         
  30.         root=gt_iget(sb,GT_ROOT_INO);//GT_ROOT_INO为1,读入文件系统的根目录
  31.         if(IS_ERR(root)){
  32.                 ret=PTR_ERR(root);
  33.                 printk(KERN_ERR "GT-fs: can't find root inode\n");
  34.                 goto failed_mount;        
  35.         }
  36.         if (!S_ISDIR(root->i_mode) || !root->i_blocks || !root->i_size) {
  37.                 iput(root);
  38.                 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);
  39.                 printk(KERN_ERR "GT-fs: corrupt root inode\n");
  40.                 goto failed_mount;
  41.         }

  42.         sb->s_root = d_alloc_root(root);//设置超级块的根目录
  43.         if (!sb->s_root) {
  44.                 iput(root);
  45.                 printk(KERN_ERR "GT: get root inode failed\n");
  46.                 ret = -ENOMEM;
  47.                 goto failed_mount;
  48.         }
  49.         
  50.         return 0;
  51. cantfind_gt:
  52.         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);
  53. failed_mount:
  54.         brelse(bh);
  55. out_bad_hblock:
  56.         printk("GT-fs:blocksize too small for device\n");
  57. failed_sbi:
  58.         sb->s_fs_info=NULL;
  59.         kfree(sbi);
  60.         return ret;
  61. }
复制代码
这段函数主要是从磁盘读入文件系统的超级块,用来填充内存中的GTFS超级块,和VFS超级块
调用gt_iget函数读入指定文件系统的根目录。
作者: goter    时间: 2009-05-23 14:45
文件系统在kernel中被当作一个模块实现
也有module_init和module_exit(定义在super.c)
  1. module_init(init_gt_fs)
  2. module_exit(exit_gt_fs)
复制代码
init_gt_fs(定义在super.c)
  1. static int __init init_gt_fs(void){
  2.     int err=init_inodecache();
  3.     if(err)
  4.        return err;
  5.     err=register_filesystem(&gt_fs_type);//向内核注册GT文件系统
  6.     if(err)
  7.        goto out;
  8.     return 0;
  9. out:
  10.     destroy_inodecache();
  11.     return err;
  12. }
复制代码
init_gt_fs中调用init_inodecache创建GTFS的内存索引节点缓冲池(我瞎猜的...对这个不了解..)

init_inodecache(定义在super.c中)
  1. static struct kmem_cache *gt_inode_cachep;
  2. static int init_inodecache(void){
  3.    gt_inode_cachep=kmem_cache_create("gt_inode_cache",sizeof(struct gt_inode_info),0,(SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD),init_once);
  4.    if(gt_inode_cachep==NULL)
  5.       return -ENOMEM;
  6.    return 0;
  7. }
复制代码
然后就是exit_gt_fs(定义在super.c中)
  1. static void __exit exit_gt_fs(void){
  2.      unregister_filesystem(&gt_fs_type);//注销文件系统
  3.      destroy_inodecache();//销毁缓冲池
  4. }
复制代码

[ 本帖最后由 goter 于 2009-5-23 14:49 编辑 ]
作者: goter    时间: 2009-05-23 14:49
接下来是超级块操作(定义在super.c中)
  1. static const struct super_operations gt_sops={
  2.         .alloc_inode        =gt_alloc_inode,//分配一个GTFS的索引节点
  3.         .destroy_inode        =gt_destroy_inode,//销毁索引节点
  4.         .write_inode        =gt_write_inode,//写入索引节点
  5.         .delete_inode        =gt_delete_inode,//删除索引节点
  6.         .write_super    =gt_write_super,//将超级块写入磁盘
  7.         .put_super        =gt_put_super,//释放超级块
  8.         .statfs                =gt_statfs,//获取文件系统状态
  9.         .write_inode        =gt_write_inode,//将索引节点写入磁盘
  10. };
复制代码
下面我们挨个介绍
gt_alloc_inode(定义在super.c中)
  1. static struct inode *gt_alloc_inode(struct super_block *sb){
  2.         struct gt_inode_info *gi;

  3.         gi=(struct gt_inode_info *)kmem_cache_alloc(gt_inode_cachep,GFP_KERNEL);//在缓冲池分配一个GTFS的内存索引节点
  4.         if(!gi)
  5.                 return NULL;
  6.         return &gi->vfs_inode;
  7. }
复制代码
函数gt_destroy_inode(定义在super.c中)
  1. static void gt_destroy_inode(struct inode *inode){
  2.         kmem_cache_free(gt_inode_cachep,GT_I(inode));
  3. }
复制代码
从缓冲池中释放GTFS的内存中索引节点
GT_I是定义在gt.h中的一个inline函数,根据VFS索引节点返回GTFS的内存索引节点
  1. static inline struct gt_inode_info *GT_I(struct inode *inode){
  2.           return container_of(inode,struct gt_inode_info,vfs_inode);
  3. }
复制代码
然后是gt_write_inode(定义在super.c中)
  1. static int gt_write_inode(struct inode *inode,int wait){
  2.         brelse(gt_update_inode(inode));
  3.         return 0;
  4. }
复制代码
可见,gt_write_inode函数调用gt_update_inode来将内存中的索引节点写入磁盘

gt_update_inode(定义在inode.c中)
  1. struct buffer_head *gt_update_inode(struct inode *inode){
  2.                
  3.         struct gt_inode_info *gi=GT_I(inode);
  4.         struct super_block *sb=inode->i_sb;
  5.         ino_t ino=inode->i_ino;
  6.         uid_t uid=inode->i_uid;
  7.         gid_t gid=inode->i_gid;
  8.         struct buffer_head *bh;
  9.         struct gt_inode *raw_inode =gt_raw_inode(sb,ino,&bh);//根据超级块和索引节点号从磁盘读入GTFS的磁盘索引节点

  10.         if(!raw_inode)
  11.                 return NULL;
  12.         /*更新*/
  13.         raw_inode->i_mode=inode->i_mode;
  14.         raw_inode->i_uid=uid;
  15.         raw_inode->i_gid=gid;
  16.         raw_inode->i_nlinks=inode->i_nlink;
  17.         raw_inode->i_size=inode->i_size;
  18.         raw_inode->i_atime=inode->i_atime.tv_sec;
  19.         raw_inode->i_mtime=inode->i_mtime.tv_sec;
  20.         raw_inode->i_ctime=inode->i_ctime.tv_sec;
  21.         //raw_inode->i_dtime=inode->i_dtime.tv_sec;
  22.        
  23.         if(S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
  24.                 raw_inode->i_dev=old_encode_dev(inode->i_rdev);
  25.         else{
  26.                 raw_inode->i_start_block=gi->i_start_block;
  27.                 raw_inode->i_end_block=gi->i_end_block;
  28.                 raw_inode->i_blocks=gi->i_blocks;
  29.                 raw_inode->i_reserved=gi->i_reserved;
  30.                        
  31.         }
  32.         mark_buffer_dirty(bh);//将磁盘索引节点所在的缓冲块标记为脏
  33.         return bh;
  34. }
复制代码
gt_update_inode很简单,通过调用gt_raw_inode来读入GTFS的磁盘索引节点,然后根据GTFS内存索引节点去设置磁盘索引节点,最后将磁盘索引节点所在的缓冲块标记为脏,等待机会写入磁盘。gt_raw_inode将在后面介绍,我们回到超级块操作来

接下来是gt_delete_inode(定义在super.c中)
  1. static void gt_delete_inode(struct inode *inode){
  2.         truncate_inode_pages(&inode->i_data,0);
  3.         GT_I(inode)->i_dtime=get_seconds();
  4.         inode->i_size=0;
  5.         gt_truncate(inode);//清空文件
  6.         gt_free_inode(inode);//释放索引节点
  7. }
复制代码
让我们来看看gt_truncate(定义在inode.c中)
  1. void gt_truncate(struct inode *inode){
  2.         if(!(S_ISREG(inode->i_mode)||S_ISDIR(inode->i_mode)||S_ISLNK(inode->i_mode)))
  3.                 return;
  4.         struct gt_inode_info *gi=GT_I(inode);
  5.         block_truncate_page(inode->i_mapping,inode->i_size,gt_get_block);

  6.         gi->i_reserved+=gi->i_end_block-gi->i_start_block+1;//设置预留块数
  7.         gi->i_end_block=gi->i_start_block;//清空
  8.         inode->i_mtime=inode->i_ctime=CURRENT_TIME_SEC;
  9.         mark_inode_dirty(inode);
  10. }
复制代码
这个函数主要是设置内存索引节点的块使用状况

还有gt_free_inode(定义在inode.c中)
  1. void gt_free_inode(struct inode *inode){
  2.         struct super_block *sb=inode->i_sb;
  3.         struct gt_super_block *gs=GT_SB(inode->i_sb)->s_gs;
  4.         struct buffer_head *bh;
  5.         unsigned long ino;
  6.         ino=inode->i_ino;
  7.         struct gt_inode *raw_inode=NULL;
  8.         if(ino<1||ino>gs->s_inodes_count){
  9.                 printk("gt_free_inode: inode 0 or nonexistent inode\n");
  10.                 return;
  11.         }
  12.         raw_inode=gt_raw_inode(sb,ino,&bh);
  13.         if(raw_inode){
  14.                 raw_inode->i_nlinks=0;//设置磁盘索引节点的连接数
  15.                 raw_inode->i_mode=0;
  16.         }
  17.         if(bh){
  18.                 mark_buffer_dirty(bh);
  19.                 brelse(bh);
  20.         }
  21.         clear_inode(inode);//调用VFS函数清理VFS索引节点
  22. }
复制代码
这个函数也很简单,只是读取磁盘索引节点,然后设置连接数和模式,然后标记磁盘索引节点所在的缓冲块为脏
作者: goter    时间: 2009-05-23 14:51
接着写超级块操作
接下来就是gt_write_super(定义在super.c中)
  1. static int gt_write_super(struct super_block *sb){
  2.         struct gt_super_block *gs;
  3.         lock_kernel();
  4.         gs=GT_SB(sb)->s_gs;
  5.         gs->s_free_blocks_count=cpu_to_le32(gt_count_free_blocks(sb));
  6.         gs->s_free_inodes_count=cpu_to_le32(gt_count_free_inodes(sb));
  7.         gs->s_mtime=cpu_to_le32(get_seconds());
  8.         gs->s_wtime = cpu_to_le32(get_seconds());
  9.         mark_buffer_dirty(GT_SB(sb)->s_sbh);
  10.         sync_dirty_buffer(GT_SB(sb)->s_sbh);
  11.         sb->s_dirt=0;       
  12.         unlock_kernel();
  13. }
复制代码
这个很简单很简单,简单到我都懒的讲了..
让我们来看看gt_count_free_blocks和gt_count_free_inodes
这俩函数都定义在inode.c中
  1. unsigned long gt_count_free_inodes(struct super_block *sb){
  2.         struct buffer_head *bh;
  3.         struct gt_inode *gt;
  4.         char *p;
  5.          
  6.         unsigned long block=2; //索引节点表所在块
  7.         unsigned long count=0;//使用了的索引节点数
  8.         //然后遍历索引节点表
  9.         while(bh=sb_bread(sb,block)){
  10.                 p=bh->b_data;
  11.                 while(p<=(bh->b_data+GT_BLOCK_SIZE-GT_INODE_SIZE)){
  12.                         gt=(struct gt_inode *)p;
  13.                         if(gt->i_nlinks)
  14.                                 count++;//已经使用的索引节点数加一
  15.                         p+=GT_INODE_SIZE;
  16.                 }
  17.                 brelse(bh);
  18.                 if(block>GT_INODE_BLOCK_COUNT(sb))//如果到了索引节点表结尾则跳出
  19.                         break;
  20.                 block++;       
  21.         }
  22.        
  23.         return GT_SB(sb)->s_gs->s_inodes_count-count;//返回未使用的索引节点数
  24. }
复制代码
  1. unsigned long gt_count_free_blocks(struct super_block *sb){

  2.         struct gt_super_block *gs;
  3.         char *p;
  4.         int block=2;
  5.         gs=GT_SB(sb)->s_gs;
  6.         unsigned long used=0;//已经使用的块数
  7.         struct buffer_head *bh;
  8.         struct gt_inode * gt;
  9.        //遍历索引节点表,已经使用的块数其实就等于最后一个索引节点的i_end_block
  10.         while(bh=sb_bread(sb,block)){
  11.                 p=bh->b_data;
  12.                 while(p<=(bh->b_data+GT_BLOCK_SIZE-GT_INODE_SIZE)){
  13.                         gt=(struct gt_inode *)p;
  14.                         if(!gt->i_blocks)
  15.                                 used=gt->i_end_block;
  16.                                
  17.                 }
  18.                 brelse(bh);
  19.         }
  20.         return GT_BLOCKS(sb)-used;
  21. }
复制代码

作者: goter    时间: 2009-05-23 14:55
gt_put_super(定义在super.c中)
  1. static void gt_put_super(struct super_block *sb){
  2.         struct gt_sb_info *sbi=GT_SB(sb);
  3.         brelse(sbi->s_sbh);
  4.         sb->s_fs_info=NULL;
  5.         kfree(sbi);
  6. }
复制代码
这个函数释放掉磁盘超级块所在的缓冲块,释放掉内存超级块

gt_statfs(定义在super.c中)
  1. static int gt_statfs(struct dentry *dentry,struct kstatfs *buf){
  2.         struct gt_sb_info * sbi=GT_SB(dentry->d_sb);
  3.         struct gt_super_block *gs=sbi->s_gs;
  4.         buf->f_type=dentry->d_sb->s_magic;
  5.         buf->f_bsize=dentry->d_sb->s_blocksize;
  6.         buf->f_blocks=(gs->s_blocks_count-gs->s_first_data_block);
  7.         buf->f_bfree=gt_count_free_blocks(sbi);
  8.         buf->f_bavail=buf->f_bfree;
  9.         buf->f_ffree=gt_count_free_inodes(sbi);
  10.         buf->f_namelen=GT_NAME_LEN;
  11.         return 0;
  12. }
复制代码
这个函数获取文件系统状态

到这里超级块的操作就完成了,接下来是普通索引节点操作,只需要一个清空函数,其他的调用vfs默认的函数
  1. const struct inode_operations gt_file_inode_operations ={
  2.         .truncate        =gt_truncate,
  3. };
复制代码

[ 本帖最后由 goter 于 2009-5-23 15:03 编辑 ]
作者: goter    时间: 2009-05-23 15:02
普通文件操作(在file.c中定义),只需要实现一个gt_sync_file
  1. const struct file_operations gt_file_operations ={
  2.         .llseek                =generic_file_llseek,
  3.         .read                =do_sync_read,
  4.         .write                =do_sync_write,
  5.         .aio_read        =generic_file_aio_read,
  6.         .aio_write        =generic_file_aio_write,
  7.         .mmap                =generic_file_mmap,
  8.         .open                =generic_file_open,
  9.         .fsync                =gt_sync_file,
  10. };
复制代码


来看看gt_sync_file(在file.c中定义)
  1. int gt_sync_file(struct file *file,struct dentry *dentry,int datasync){
  2.         struct inode *inode =dentry->d_inode;
  3.         int err,ret;
  4.         ret=sync_mapping_buffers(inode->i_mapping);
  5.         if(!(inode->i_state&I_DIRTY))
  6.                 return ret;
  7.         if(datasync && !(inode->i_state&I_DIRTY_DATASYNC))
  8.                 return ret;
  9.         err=gt_sync_inode(inode);
  10.         if(ret==0)
  11.                 ret=err;
  12.         return ret;
  13. }
复制代码

函数调用gt_sync_inode(在inode.c中定义)
  1. int gt_sync_inode(struct inode *inode){
  2.         int ret=0;
  3.         struct buffer_head *bh;
  4.         bh=gt_update_inode(inode);//获取到磁盘索引节点所在的缓冲区
  5.         if(bh && buffer_dirty(bh)){//如果为脏,则同步
  6.                 sync_dirty_buffer(bh);
  7.                 if(buffer_req(bh)&&!buffer_uptodate(bh)){
  8.                         printk("IO error syncing gt inode\n");
  9.                         ret=-1;
  10.                 }
  11.         }else if(!bh)
  12.                 ret=-1;
  13.         brelse(bh);
  14.         return ret;
  15. }
复制代码

[ 本帖最后由 goter 于 2009-5-23 15:09 编辑 ]
作者: goter    时间: 2009-05-23 15:09
未完待续
作者: Godbach    时间: 2009-05-23 15:15
多谢LZ分享,改天有时间编译一下源码,试用一下。
作者: tastesweet    时间: 2009-05-23 15:36
时间试用下  赞
作者: goter    时间: 2009-05-23 18:27
然后就是目录操作(在dir.c中定义)
  1. const struct file_operations gt_dir_operations ={
  2.         .llseek        =generic_file_llseek,
  3.         .read                =generic_read_dir,
  4.         .readdir        =gt_readdir,
  5.         .fsync        =gt_sync_file,
  6. };
复制代码


gt_readdir(在dir.c中定义)
linux文件系统对文件的读写是以页为单位的。
  1. static int gt_readdir(struct file *filp,void *dirent,filldir_t filldir){
  2.         loff_t pos=filp->f_pos;//目前读到的位置,文件内偏移
  3.        
  4.         struct inode *inode=filp->f_path.dentry->d_inode;//目录文件的inode
  5.        
  6.         unsigned int offset=pos & ~PAGE_CACHE_MASK;
  7.         unsigned long n=pos >> PAGE_CACHE_SHIFT;//目前读到第几页
  8.         unsigned long npages=gt_dir_pages(inode);//该文件在内存中占用的页数
  9.         unsigned chunk_size=sizeof(struct gt_dir_entry);
  10.         lock_kernel();
  11.         pos=(pos+chunk_size-1)&~(chunk_size-1);//将目前读到的位置调整到目录项开始
  12.         if(pos>=inode->i_size)
  13.                 goto done;       
  14.         for(;n<npages;n++,offset=0){//遍历文件所占用的页
  15.                 char *kaddr,*limit;
  16.                 char *p;
  17.        
  18.                 struct page *page=gt_get_page(inode,n);//获取到第n页
  19.        
  20.                 if(IS_ERR(page)){
  21.                         continue;
  22.                         printk("page is error\n");
  23.                 }
  24.        
  25.                 kaddr=(char *)page_address(page);//第n页在内存中的地址
  26.        
  27.                 p=(kaddr+offset);//指向当前该读入的目录项的地址
  28.        
  29.                 limit=kaddr+gt_last_byte(inode,n)-chunk_size;//边界
  30.                 for(;p<=limit;p=gt_next_entry(p)){
  31.                         struct gt_dir_entry * de=(struct gt_dir_entry*)p;
  32.                           
  33.                         if(de->ino){//目录项有对应的索引节点号
  34.                                        
  35.                                 offset=p -kaddr;//在页内的偏移
  36.                                 unsigned name_len=strnlen(de->name,GT_NAME_LEN);
  37.                                 unsigned char d_type=DT_UNKNOWN;
  38.                                 int over=filldir(dirent,de->name,name_len,(n<<PAGE_CACHE_SHIFT)|offset,le32_to_cpu(de->ino),d_type);//调用VFS函数填充dirent结构,有兴趣的朋友可以自己去研究
  39.                                 if(over){
  40.                                         gt_put_page(page);//如果错误就释放该页,跳到done标号处
  41.                                         goto done;
  42.                                 }
  43.                         }                       
  44.                 }
  45.                 gt_put_page(page);
  46.         }
  47. done:
  48.         filp->f_pos=(n<<PAGE_CACHE_SHIFT)|offset;//调整当前文件内偏移
  49.         unlock_kernel();
  50.         return 0;       
  51. }
复制代码

这段函数调用了几个新函数,主要是使用作为参数传递进来的filldir函数来完成gt_readdir
下面来看看这段函数中调用的新函数
gt_dir_pages(在dir.c中定义)
  1. static inline unsigned long gt_dir_pages(struct inode *inode){
  2.         return (inode->i_size+PAGE_CACHE_SIZE-1)>>PAGE_CACHE_SHIFT;
  3. }
复制代码

很简单,就不解释了
gt_get_page(在dir.c中定义)
  1. static struct page *gt_get_page(struct inode *dir,unsigned long n){
  2.         struct address_space *mapping=dir->i_mapping;
  3.         struct page *page=read_mapping_page(mapping,n,NULL);
  4.         if(!IS_ERR(page)){
  5.                 kmap(page);
  6.                 if(!PageUptodate(page))
  7.                         goto fail;
  8.         }
  9.         return page;
  10. fail:
  11.         gt_put_page(page);
  12.         return ERR_PTR(-EIO);
  13. }
复制代码

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

gt_last_byte(在dir.c中定义)
  1. static unsigned gt_last_byte(struct inode *inode, unsigned long page_nr){
  2.         unsigned last_byte = inode->i_size;

  3.         last_byte -= page_nr << PAGE_CACHE_SHIFT;
  4.         if (last_byte > PAGE_CACHE_SIZE)
  5.                 last_byte = PAGE_CACHE_SIZE;
  6.         return last_byte;
  7. }
复制代码

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

gt_next_entry(在dir.c中定义)
  1. static inline void *gt_next_entry(void *de){
  2.         return (void *)((char *)de+sizeof(struct gt_dir_entry));
  3. }
复制代码

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

gt_sync_file在前边已经分析完
这样目录操作也分析完了,下边是地址空间操作gt_aops
作者: goter    时间: 2009-05-23 18:44
因为地址空间操作的操作对象是内存,与磁盘的组织方式无关,所以这个我们可以直接使用VFS的地址空间操作,这样就简单多了
gt_aops(在inode.c中定义)
  1. const struct address_space_operations gt_aops ={
  2.         .readpage                =gt_readpage,
  3.         .writepage                =gt_writepage,
  4.         .sync_page                =block_sync_page,
  5.         .write_begin                =gt_write_begin,
  6.         .write_end                =generic_write_end,
  7.         .bmap                        =gt_bmap,
  8. };
复制代码

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

  1. static int gt_writepage(struct page *page,struct writeback_control *wbc){

  2.         return block_write_full_page(page,gt_get_block,wbc);
  3. }

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

  7. int __gt_write_begin(struct file *file,struct address_space *mapping,loff_t pos,unsigned len,unsigned flags,struct page *pagep,void **fsdata){
  8.         return block_write_begin(file,mapping,pos,len,flags,pagep,fsdata,gt_get_block);

  9. }

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

  15. static sector_t gt_bmap(struct address_space *mapping,sector_t block){
  16.         return generic_block_bmap(mapping,block,gt_get_block);
  17. }
复制代码

地址空间操作就是这样,接下来是对目录索引节点的操作
作者: flike    时间: 2009-05-23 19:04
提示: 作者被禁止或删除 内容自动屏蔽
作者: goter    时间: 2009-05-23 19:06
标题: 回复 #10 Godbach 的帖子
哈哈,谢谢Godbach大哥
作者: goter    时间: 2009-05-23 19:07
标题: 回复 #14 flike 的帖子
需要重新编译内核,版本的话我是用的2.6.27.8,相近的版本应该也可以
作者: Godbach    时间: 2009-05-23 19:13
原帖由 goter 于 2009-5-23 19:07 发表
需要重新编译内核,版本的话我是用的2.6.27.8,相近的版本应该也可以


需要重新编译内核啊。之前论坛上有另外一个网友上传了一个比较简单的Ramdisk,好像可以按照内核模块的方式加载到内核。
作者: flike    时间: 2009-05-23 19:17
提示: 作者被禁止或删除 内容自动屏蔽
作者: goter    时间: 2009-05-23 19:26
标题: 回复 #17 Godbach 的帖子
我去搜搜看
作者: goter    时间: 2009-05-23 19:28
标题: 回复 #18 flike 的帖子
gt_fs.h需要放到include/linux下
mkfs.gt是个二进制文件,你可以把它放到任意位置,只支持mkfs.gt /dev/sdax
需要代码的话我可以贴上来

作者: flike    时间: 2009-05-23 19:31
提示: 作者被禁止或删除 内容自动屏蔽
作者: zenglei421    时间: 2009-05-23 19:50
我来学习 哈哈 谢谢楼主呀
。。
作者: zenglei421    时间: 2009-05-23 20:03
怎么有点类似 驱动呀
哈哈
对内核不是很熟悉哈
难道 文件系统也是模块化编程里的一种?
呵呵 新手
作者: flike    时间: 2009-05-23 20:06
提示: 作者被禁止或删除 内容自动屏蔽
作者: goter    时间: 2009-05-23 20:21
标题: 回复 #24 flike 的帖子
我重新上传了个压缩包,里边有mkfs.gt的代码
调试的话是按albcamus老大写的教程来的,很好用,地址http://linux.chinaunix.net/bbs/v ... p%3Bfilter%3Ddigest
作者: goter    时间: 2009-05-23 21:08
下面我们来分析目录索引节点操作,这个应该是文件系统中最重要的地方了
在namei.c中定义
  1. const struct inode_operations gt_dir_inode_operations={
  2.         .create        =gt_create,
  3.         .lookup        =gt_lookup,
  4.         .link                =gt_link,
  5.         .unlink        =gt_unlink,
  6.         .symlink        =gt_symlink,
  7.         .mkdir        =gt_mkdir,
  8.         .rmdir        =gt_rmdir,
  9.         .mknod        =gt_mknod,
  10.         .rename        =gt_rename,
  11. };
复制代码


先来看gt_create(在namei.c中定义)
  1. static int gt_create(struct inode *dir,struct dentry *dentry,int mode,struct nameidata *nd){
  2.         return gt_mknod(dir,dentry,mode,0);
  3. }
复制代码

gt_create调用gt_mknod在dir中创建一个文件

gt_mknod(在namei.c中定义)
  1. static int gt_mknod(struct inode *dir,struct dentry *dentry,int mode,dev_t rdev){
  2.         int error;
  3.         struct inode *inode;
  4.         if(!old_valid_dev(rdev))
  5.                 return -EINVAL;
  6.          
  7.         inode=gt_new_inode(dir,&error);//在dir代表的目录中新建一个索引节点
  8.         if(inode){
  9.                 inode->i_mode=mode;
  10.                 gt_set_inode(inode,rdev);//根据素引节点类型,设置不同的操作
  11.                
  12.                 mark_inode_dirty(inode);
  13.                 error=add_nondir(dentry,inode);//将目录项和索引节点联系起来
  14.         }
  15.         return error;
  16. }
复制代码

这个函数调用了gt_new_inode,gt_set_inode,add_nondir这三个函数
先来看gt_new_inode(在inode.c中定义)
  1. struct inode *gt_new_inode(struct inode *dir,int *err){

  2.         struct super_block *sb;
  3.         struct buffer_head *bh;
  4.         ino_t ino=0;
  5.         int block;
  6.         struct inode *inode;

  7.         struct gt_inode_info *gi;
  8.         struct gt_inode *raw_inode;
  9.         char *p;
  10.        
  11.         sb=dir->i_sb;
  12.         inode=new_inode(sb);//调用VFS函数,VFS函数又会调用前面分析过的超级块操作中的gt_alloc_inode函数来在内存中分配一个索引节点
  13.         if(!inode)
  14.                 return ERR_PTR(-ENOMEM);
  15.         gi=GT_I(inode);//获取到内存索引节点       
  16.        
  17.         inode->i_uid=current->fsuid;
  18.         inode->i_gid=(dir->i_mode&S_ISGID)?dir->i_gid:current->fsgid;
  19.         inode->i_mtime=inode->i_atime=inode->i_ctime=CURRENT_TIME_SEC;
  20.        
  21.         block=2;//索引节点表所在的块
  22.         struct gt_inode *prev=NULL;
  23.         while(bh=sb_bread(sb,block)){//遍历索引节点表
  24.                 p=bh->b_data;
  25.                 while(p<=(bh->b_data+GT_BLOCK_SIZE-GT_INODE_SIZE)){
  26.                         raw_inode=(struct gt_inode *)p;
  27.                         ino++;
  28.                         if(!raw_inode->i_nlinks&&!raw_inode->i_start_block){//找到一个链接数为0,而且,开始块为0的磁盘索引节点
  29.                                 if(!prev->i_reserved)//如果前一个索引节点的i_reserved为0,则设置其为默认的GT_BLOCK_RESERVED
  30.                                         prev->i_reserved=GT_BLOCK_RESERVED;
  31.                                 prev->i_blocks=prev->i_end_block-prev->i_start_block+1;//并且设置其i_blocks,表示该索引节点已经封顶
  32.                        
  33.                                 mark_buffer_dirty(bh);
  34.                                 goto find;
  35.                         }
  36.                         p+=GT_INODE_SIZE;
  37.                        
  38.                         prev=raw_inode;
  39.                 }
  40.                 brelse(bh);
  41.                 if(block>GT_INODE_BLOCK_COUNT(sb))
  42.                         break;
  43.                 block++;       
  44.         }
  45.        
  46.         iput(inode);
  47.         brelse(bh);
  48.         *err=-ENOSPC;
  49.         return NULL;
  50. find:
  51.         inode->i_ino=ino;
  52.        
  53.         raw_inode->i_start_block=prev->i_end_block+prev->i_reserved+1;//新磁盘索引节点的开始块等于前一个索引节点的结束块加上预留块
  54.         gi->i_reserved=raw_inode->i_reserved;//根据新磁盘索引节点来设置内存索引节点
  55.         gi->i_start_block=gi->i_end_block=raw_inode->i_start_block;
  56.         raw_inode->i_end_block=raw_inode->i_start_block;//新磁盘索引节点的开始块等于结束块,此时并不设置i_blocks,因为这个索引节点是新建的,不需要被“封顶”
  57.         brelse(bh);
  58.         insert_inode_hash(inode);       
  59.         mark_inode_dirty(inode);
  60.         *err=0;
  61.         return inode;
  62. }
复制代码


然后就是gt_set_inode(在namei.c中定义)
  1. void gt_set_inode(struct inode *inode,dev_t rdev){
  2.         if(S_ISREG(inode->i_mode)){
  3.                 inode->i_op=&gt_file_inode_operations;
  4.                 inode->i_fop=&gt_file_operations;
  5.                 inode->i_mapping->a_ops=&gt_aops;
  6.         }else if(S_ISDIR(inode->i_mode)){
  7.                 inode->i_op=&gt_dir_inode_operations;
  8.                 inode->i_fop=&gt_dir_operations;
  9.                 inode->i_mapping->a_ops=&gt_aops;
  10.         }else if(S_ISLNK(inode->i_mode)){
  11.                 inode->i_op=&gt_symlink_inode_operations;
  12.                 inode->i_mapping->a_ops=&gt_aops;
  13.         }else
  14.                 init_special_inode(inode,inode->i_mode,rdev);
  15. }
复制代码

这个函数函数很重要,它根据索引节点的属性,设置其对应的操作方法,从而将VFS的操作与GTFS的操作联系起来

add_nondir(在namei.c中定义)函数
  1. static int add_nondir(struct dentry *dentry,struct inode *inode){
  2.        
  3.         int err=gt_add_link(dentry,inode);
  4.         if(!err){
  5.                 d_instantiate(dentry,inode);//成功的话,将dentry链入inode对应的目录项队列中(一个索引节点可以对应很多目录项)
  6.                 return 0;       
  7.         }
  8.         inode_dec_link_count(inode);//如果出错,则该索引节点的链接数应该减一
  9.         iput(inode);
  10.         return err;
  11. }
复制代码

这个函数又调用gt_add_link来将dentry对应的目录项加入父目录,然后设置目录项对应的索引节点号
下面就是gt_add_link
  1. int gt_add_link(struct dentry *dentry,struct inode *inode){
  2.         struct inode *dir=dentry->d_parent->d_inode;//获取到父目录
  3.         const char *name=dentry->d_name.name;//目录项名称
  4.         int namelen=dentry->d_name.len;

  5.         struct page *page=NULL;
  6.         unsigned long npages=gt_dir_pages(dir);
  7.         unsigned long n;
  8.         char *kaddr,*p;
  9.         int ino;
  10.         struct gt_dir_entry *de;
  11.         loff_t pos;
  12.         int err;
  13.         char *namx=NULL;
  14.         __u32 inumber;

  15.         for(n=0;n<=npages;n++){
  16.                 char *limit,*dir_end;
  17.                 page=gt_get_page(dir,n);
  18.                 err=PTR_ERR(page);
  19.                 if(IS_ERR(page))
  20.                         goto out;
  21.                 lock_page(page);
  22.                 kaddr=(char *)page_address(page);
  23.                 dir_end=kaddr+gt_last_byte(dir,n);//在页内未使用空间的地址
  24.                 limit=kaddr+PAGE_CACHE_SIZE-sizeof(struct gt_dir_entry);
  25.                 for(p=kaddr;p<=limit;p=gt_next_entry(p)){
  26.                         de=(struct gt_dir_entry *)p;
  27.                         namx=de->name;
  28.                         inumber=de->ino;
  29.                         if(p==dir_end){//如果走到最后一个页的目录项结尾,则在此处插入新的目录项
  30.                                 goto got_it;
  31.                         }
  32.                         if(!inumber)//如果在中途某个目录项没有被使用,对应的索引节点号为0(被删除),则跳到got_it
  33.                                 goto got_it;

  34.                         err=-EEXIST;
  35.                         if(namecompare(namelen,GT_NAME_LEN,name,namx))
  36.                                 goto out_unlock;
  37.                         ino=de->ino;
  38.                 }
  39.                 unlock_page(page);
  40.                 gt_put_page(page);
  41.         }
  42.         BUG();
  43.         return -EINVAL;
  44. got_it:
  45. //准备开始写,pos为页内偏移,在pos开始处写
  46.         pos=(page->index>>PAGE_CACHE_SHIFT)+p-(char *)page_address(page);
  47.         err=__gt_write_begin(NULL,page->mapping,pos,sizeof(struct gt_dir_entry),AOP_FLAG_UNINTERRUPTIBLE,&page,NULL);
  48.         if(err)
  49.                 goto out_unlock;
  50.         memcpy(namx,name,namelen);       
  51.         memset(namx+namelen,0,GT_NAME_LEN-namelen-4);// 将名字数组多余的空间清空
  52.         de->ino=inode->i_ino;//参数inode用来设置目录项对应的索引节点号
  53.         err=gt_commit_chunk(page,pos,sizeof(struct gt_dir_entry));//提交写
  54.         dir->i_mtime=dir->i_ctime=CURRENT_TIME_SEC;
  55.         mark_inode_dirty(dir);
  56. out_put:
  57.         gt_put_page(page);
  58. out:
  59.         return err;
  60. out_unlock:
  61.         unlock_page(page);
  62.         goto out_put;
  63. }
复制代码

这个函数用来设置目录想的名称和对应的索引节点号,并且将目录项写入父目录的块里
作者: goter    时间: 2009-05-24 08:26
然后是gt_look_up(在namei.c中定义)
  1. static struct dentry *gt_lookup(struct inode *dir,struct dentry *dentry,struct nameidata *nd){
  2.         struct inode *inode=NULL;
  3.         ino_t ino=0;
  4.         dentry->d_op=dir->i_sb->s_root->d_op;
  5.         if(dentry->d_name.len>GT_NAME_LEN)
  6.                 return ERR_PTR(-ENAMETOOLONG);
  7.         struct page *page;
  8.        
  9.         struct gt_dir_entry *de=gt_find_entry(dentry,&page);
  10.         if(de){
  11.                 ino=de->ino;
  12.                 gt_put_page(page);       
  13.         }
  14.         if(ino){
  15.                 inode=gt_iget(dir->i_sb,ino);
  16.                 if(IS_ERR(inode))
  17.                         return ERR_CAST(inode);
  18.         }
  19.         d_add(dentry,inode);
  20.         return NULL;
  21. }
复制代码

顾名思义,这个函数就是要在dir目录里寻找名为dentry->d_name.name的目录项,只不过这个寻找的过程由gt_find_entry完成,如果寻找到的话,则调用gt_iget,读入该目录项对应的索引节点。并且调用d_add函数将该目录项链入索引节点。

gt_find_entry(在dir.c中定义)
  1. struct gt_dir_entry * gt_find_entry(struct dentry *dentry,struct page **res_page){
  2.         const char *name=dentry->d_name.name;
  3.         int namelen=dentry->d_name.len;
  4.         struct inode *dir=dentry->d_parent->d_inode;//父目录
  5.        
  6.         unsigned long n;
  7.         unsigned long npages=gt_dir_pages(dir);
  8.         struct page *page=NULL;
  9.         char *p;

  10.         char *namx;
  11.         __u32        inumber;
  12.         *res_page=NULL;
  13.        
  14.         for(n=0;n<npages;n++){
  15.                 char *kaddr,*limit;
  16.                 page=gt_get_page(dir,n);        //获取到父目录代表文件在内存中的第n页
  17.                 if(IS_ERR(page))
  18.                         continue;
  19.                 kaddr=(char *)page_address(page);
  20.                 limit=kaddr+gt_last_byte(dir,n)-sizeof(struct gt_dir_entry);
  21.                 for(p=kaddr;p<=limit;p=gt_next_entry(p)){
  22.                         struct gt_dir_entry *de=(struct gt_dir_entry *)p;
  23.                         namx=de->name;
  24.                         inumber=de->ino;
  25.                         if(!inumber)//如果该目录项没有被使用,跳过
  26.                                 continue;
  27.                         if(namecompare(namelen,GT_NAME_LEN,name,namx))//判断是否匹配
  28.                                 goto found;//如果匹配则调到found处
  29.                 }
  30.                 gt_put_page(page);
  31.         }
  32.         return NULL;
  33. found:
  34.         *res_page=page;//设置参数返回值
  35.         return (struct gt_dir_entry *)p;
  36. }
复制代码

这段代码和我们之前看过的代码很象,都是循环遍历,所以也没什么好讲的
接下来是
gt_iget(在inode.c中定义)
  1. struct inode *gt_iget(struct super_block *sb,unsigned long ino){

  2.         struct gt_inode_info *gi;
  3.         struct buffer_head *bh;
  4.         struct gt_inode *raw_inode;
  5.        
  6.         long ret=-EIO;
  7.          
  8.         struct inode *inode=iget_locked(sb,ino);

  9.         if(!inode)
  10.                 return ERR_PTR(-ENOMEM);
  11.         if(!(inode->i_state &I_NEW))
  12.                 return inode;
  13.         gi=GT_I(inode);

  14.         raw_inode=gt_raw_inode(inode->i_sb,ino,&bh);
  15.         if(!raw_inode){
  16.                 iput(inode);
  17.                 return NULL;
  18.         }
  19.        
  20.         inode->i_mode=raw_inode->i_mode;
  21.         inode->i_uid=(uid_t)raw_inode->i_uid;
  22.         inode->i_gid=(gid_t)raw_inode->i_gid;
  23.         inode->i_size=raw_inode->i_size;
  24.         inode->i_nlink=raw_inode->i_nlinks;
  25.         inode->i_rdev=raw_inode->i_dev;
  26.         inode->i_mtime.tv_sec = inode->i_atime.tv_sec = inode->i_ctime.tv_sec = raw_inode->i_ctime;
  27.         inode->i_mtime.tv_nsec = 0;
  28.         inode->i_atime.tv_nsec = 0;
  29.         inode->i_ctime.tv_nsec = 0;

  30.         gi->i_dtime=raw_inode->i_dtime;

  31.         if(inode->i_nlink==0 && (inode->i_mode==0||gi->i_dtime)){
  32.                
  33.                 brelse(bh);
  34.                 ret=-ESTALE;
  35.                 goto bad_inode;
  36.         }

  37.         inode->i_blocks=raw_inode->i_blocks;
  38.         gi->i_dtime=0;
  39.         gi->i_start_block=raw_inode->i_start_block;
  40.         gi->i_end_block=raw_inode->i_end_block;
  41.         gi->i_blocks=raw_inode->i_blocks;
  42.         gi->i_reserved=raw_inode->i_reserved;

  43.         gt_set_inode(inode,inode->i_rdev);
  44.         brelse(bh);
  45.         unlock_new_inode(inode);
  46.        
  47.         return inode;
  48. bad_inode:
  49.         iget_failed(inode);
  50.         return ERR_PTR(ret);
  51. }
复制代码

这个函数首先调用iget_locked来在内存中寻找超级块sb中索引节点号是ino的索引节点,如果找到就返回,如果找不到就在内存中分配一个inode结构,并且设置其索引节点号和状态,如果iget_locked返回的VFS索引节点的状态为新,说明这个VFS索引节点是在iget_locked中新分配的,需要从磁盘读入磁盘索引节点来填充VFS索引节点和内存索引节点,于是接着调用gt_raw_inode,对VFS索引节点和内存索引节点进行设置,于是,这个函数通过参数指定的超级块和索引节点号,返回在代表这个索引节点的VFS索引节点

[ 本帖最后由 goter 于 2009-5-24 08:40 编辑 ]
作者: flike    时间: 2009-05-24 12:05
提示: 作者被禁止或删除 内容自动屏蔽
作者: liuty2006    时间: 2009-05-24 20:21
关注
作者: yuanzcbbs    时间: 2009-05-25 00:44
多谢lz共享,文件数据的磁盘组织格式是什么?
作者: goter    时间: 2009-05-25 11:51
标题: 回复 #28 flike 的帖子
我是根据a老大的那片帖子搭的调试环境
一般用到的gdb的几个命令有
b 函数名(设置断点)
p 变量名 (打印变量值)
c 继续运行 (ctrl+c可以暂停程序运行)
n 执行下一条

基本上就是这些,有不懂的再问吧

作者: goter    时间: 2009-05-25 11:53
标题: 回复 #30 yuanzcbbs 的帖子
组织格式很简单,就是顺序分配块,如果没有新文件,最后一个文件可以一直获取到新块,知道磁盘最后一个块,如果有新文件的话,则设置这个文件的预留块数,这个设计是很不合理啊
作者: lubob    时间: 2009-05-25 13:06
支持楼主!
作者: 大块头    时间: 2009-05-26 13:46
简单的文件系统驱动?
作者: goter    时间: 2009-05-26 17:22
标题: 回复 #34 大块头 的帖子
文件系统和驱动还是有差别的...
作者: osmanthusgfy    时间: 2009-05-26 22:53
不管是否有使用价值,
我只想说两个字:牛人!
但希望楼主继续努力,争取有一天能够加入到linux内核中,一切皆有可能!
努力,开始了就不要放弃,希望有一天能够在linux内核中阅读到楼主的代码
作者: goter    时间: 2009-05-27 20:37
标题: 回复 #36 osmanthusgfy 的帖子
谢谢大哥支持,共同进步
作者: 谁与望天堂    时间: 2009-05-28 15:09
我最近也在做文件系统
看着romfs源码头疼
东西太多太碎,结构和函数又多,理不清关系
搞不清文件系统怎么实现,头大
作者: potti_chu    时间: 2009-05-29 00:10
收藏, 等有空,自己也做个小型文件系统试试。
作者: zhanranran    时间: 2009-05-31 15:26
标题: 问题
我运行了你的代码,但是在那可以找到include/linux,我找不到,能说一下,谢谢。
作者: goter    时间: 2009-06-01 12:20
标题: 回复 #40 zhanranran 的帖子
你得先有内核源码包,然后把内核源码包解压到/usr/src下边,内核源码包里就有include/linux
作者: 大鬼不动    时间: 2009-06-01 12:30
沙发了,不回QQ
作者: Godbach    时间: 2010-01-11 18:50
很是惭愧。本来当时是打算实际上测试一下源码,然后再加静的。结果后来有事情给忘了,也没来及加精。

好的文章,是不能埋没的。顶一下,欢迎更多学习文件系统的朋友交流。
作者: ubuntuer    时间: 2010-01-12 11:03
不错
作者: peimichael    时间: 2010-01-12 11:06
文件系统这块一直没敢碰,感觉太庞大复杂
但是又觉得不懂文件系统就不可能懂Linux。
学习
作者: goter    时间: 2010-01-18 21:53
多谢Godbach大哥,居然加精了,惭愧惭愧。
估计工作以后要做网络协议和驱动那块,也没什么机会搞文件系统了,希望对大家有帮助。
我认为文件系统相对其他内核模块来说简单很多,而且通过分析minix,ext2这些简单的文件系统就可以对整个内核编码风格,编码习惯,技巧等有深刻的理解,所以,我认为新手先阅读文件系统这部分代码绝对的物超所值。
作者: Godbach    时间: 2010-01-18 22:30
标题: 回复 #46 goter 的帖子
嗯,只要每个人都能找到自己学内核的入口就好了。。
作者: T-Bagwell    时间: 2010-01-19 11:05
向goter兄学习
作者: peimichael    时间: 2010-01-19 12:49
附件能下载了?不错
前两天还愁附件啥时候审核完呢。
作者: allenflydown    时间: 2010-01-19 15:18
顶上去
作者: danbz520    时间: 2010-05-31 22:08
高手,拜读!
作者: z85525006    时间: 2010-07-23 19:11
我感觉好像在那里看件似的,  

似乎像  Linux 内核完全剖析
作者: linux初学三月    时间: 2010-07-23 21:58
这个文件系统也不错

http://linux.chinaunix.net/bbs/thread-1167965-1-1.html
作者: EZWORD    时间: 2010-08-31 18:17
顶上去
作者: ouyangyufu    时间: 2010-09-09 17:24
好的源码分析,受益了.正在研究文件系统,非常感谢.
作者: sygspjlsj    时间: 2011-04-16 23:30
牛人啊!
作者: lizhiang028    时间: 2012-09-14 14:14
gt_new_inode

inode->i_uid=current->fsuid;
inode->i_gid=(dir->i_mode&S_ISGID)?dir->i_gid:current->fsgid;

编译出错
作者: 搭车客    时间: 2014-01-27 14:49
哈哈,拜读了
作者: humjb_1983    时间: 2014-01-27 16:34
突然看到,我也抽空拜读下。
作者: xugc_yxq    时间: 2014-02-18 14:40
多谢LZ分享,请继续分析下面的程序,好吗??我是个新手
作者: dingning239    时间: 2014-03-25 16:15
完全是看天书,一点都不懂,楼主能简洁地说一下gtfs在linux上的大概用途吗?与ext3的区别是什么
作者: 256893    时间: 2014-03-25 18:25
赞!还没找到学linux的入口,想学文件系统
作者: beyondfly    时间: 2014-07-04 09:55
好文章,强烈支持
作者: zhj1011    时间: 2014-08-13 11:10
讲解的很好,学习了。      
作者: licup123    时间: 2014-10-08 13:20
内存inode和磁盘inode有什么联系,是如何映射的?
内存inode仅仅是访问提高访问速度吗?

代码中gt_inode ,struct gt_inode_info,vfs_inode这3个数据结构是怎么联系起来的

另外磁盘超级块数据结构为何又是这样定义:
struct  gt_sb_info{
        struct gt_super_block * s_gs;   ????
        struct buffer_head * s_sbh;
};
而内存的磁盘结构要这样定义
struct gt_inode_info{
       …………       
        truct inode vfs_inode; ?????
};

小弟初学者,对这个很模糊对这几个数据结构的关系没摸清楚,请各位指教!
作者: shenyanxxxy    时间: 2014-10-08 15:18
应该把这个代码放到github上,维护起来才更有感觉。
作者: beyondfly    时间: 2015-03-07 21:08
楼主,我在2.6.36.65内核上编译错误,不知道是不是版本的问题?

fs/gt/dir.c: In function ‘gt_readdir’:
fs/gt/dir.c:72:2: error: implicit declaration of function ‘lock_kernel’ [-Werror=implicit-function-declaration]
fs/gt/dir.c:98:5: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement]
fs/gt/dir.c:111:2: error: implicit declaration of function ‘unlock_kernel’ [-Werror=implicit-function-declaration]
fs/gt/dir.c: In function ‘gt_add_link’:
fs/gt/dir.c:216:60: warning: passing argument 6 of ‘__gt_write_begin’ from incompatible pointer type [enabled by default]
In file included from fs/gt/dir.c:1:0:
fs/gt/gt.h:35:12: note: expected ‘struct page *’ but argument is of type ‘struct page **’
fs/gt/dir.c: In function ‘gt_delete_entry’:
fs/gt/dir.c:246:2: warning: passing argument 6 of ‘__gt_write_begin’ from incompatible pointer type [enabled by default]
In file included from fs/gt/dir.c:1:0:
fs/gt/gt.h:35:12: note: expected ‘struct page *’ but argument is of type ‘struct page **’
fs/gt/dir.c: In function ‘gt_make_empty’:
fs/gt/dir.c:328:69: warning: passing argument 6 of ‘__gt_write_begin’ from incompatible pointer type [enabled by default]
In file included from fs/gt/dir.c:1:0:
fs/gt/gt.h:35:12: note: expected ‘struct page *’ but argument is of type ‘struct page **’
fs/gt/dir.c:338:9: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement]
fs/gt/dir.c: In function ‘gt_set_link’:
fs/gt/dir.c:363:66: warning: passing argument 6 of ‘__gt_write_begin’ from incompatible pointer type [enabled by default]
In file included from fs/gt/dir.c:1:0:
fs/gt/gt.h:35:12: note: expected ‘struct page *’ but argument is of type ‘struct page **’
cc1: some warnings being treated as errors
make[2]: *** [fs/gt/dir.o] 错误 1
make[1]: *** [fs/gt] 错误 2
make: *** [fs] 错误 2

作者: 我爱你我的菜    时间: 2016-03-25 10:47
来学习,谢谢




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2