免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: goter
打印 上一主题 下一主题

[文件系统] 一个简单文件系统的实现 [复制链接]

论坛徽章:
1
双子座
日期:2013-10-30 14:48:40
21 [报告]
发表于 2009-05-23 19:31 |只看该作者
贴出来,毕竟是这个文件系统的一部分

论坛徽章:
0
22 [报告]
发表于 2009-05-23 19:50 |只看该作者
我来学习 哈哈 谢谢楼主呀
。。

论坛徽章:
0
23 [报告]
发表于 2009-05-23 20:03 |只看该作者
怎么有点类似 驱动呀
哈哈
对内核不是很熟悉哈
难道 文件系统也是模块化编程里的一种?
呵呵 新手

论坛徽章:
1
双子座
日期:2013-10-30 14:48:40
24 [报告]
发表于 2009-05-23 20:06 |只看该作者
想问一下楼主,你是怎样调试这个程序的,毕竟你也不能保证第一次写出来的代码就没有错误?
学习下你的经验.

论坛徽章:
2
申猴
日期:2013-12-26 22:11:31天秤座
日期:2014-12-23 10:23:19
25 [报告]
发表于 2009-05-23 20:21 |只看该作者

回复 #24 flike 的帖子

我重新上传了个压缩包,里边有mkfs.gt的代码
调试的话是按albcamus老大写的教程来的,很好用,地址http://linux.chinaunix.net/bbs/v ... p%3Bfilter%3Ddigest

论坛徽章:
2
申猴
日期:2013-12-26 22:11:31天秤座
日期:2014-12-23 10:23:19
26 [报告]
发表于 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. }
复制代码

这个函数用来设置目录想的名称和对应的索引节点号,并且将目录项写入父目录的块里

论坛徽章:
2
申猴
日期:2013-12-26 22:11:31天秤座
日期:2014-12-23 10:23:19
27 [报告]
发表于 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 编辑 ]

论坛徽章:
1
双子座
日期:2013-10-30 14:48:40
28 [报告]
发表于 2009-05-24 12:05 |只看该作者
楼主,谢谢你分享自己的作品!但有时候想改一下代码,并调试一下不知怎么弄。有时间的话请把你的内核调试环境搭建及调试方法写出来。让大家学习一下。

论坛徽章:
0
29 [报告]
发表于 2009-05-24 20:21 |只看该作者
关注

论坛徽章:
0
30 [报告]
发表于 2009-05-25 00:44 |只看该作者
多谢lz共享,文件数据的磁盘组织格式是什么?
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP