免费注册 查看新帖 |

Chinaunix

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

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

论坛徽章:
0
11 [报告]
发表于 2009-05-23 15:36 |只看该作者
时间试用下  赞

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

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

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

论坛徽章:
1
双子座
日期:2013-10-30 14:48:40
14 [报告]
发表于 2009-05-23 19:04 |只看该作者
是不是要重新编译内核,要是的话,采用什么版本?

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

回复 #10 Godbach 的帖子

哈哈,谢谢Godbach大哥

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

回复 #14 flike 的帖子

需要重新编译内核,版本的话我是用的2.6.27.8,相近的版本应该也可以

论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
17 [报告]
发表于 2009-05-23 19:13 |只看该作者
原帖由 goter 于 2009-5-23 19:07 发表
需要重新编译内核,版本的话我是用的2.6.27.8,相近的版本应该也可以


需要重新编译内核啊。之前论坛上有另外一个网友上传了一个比较简单的Ramdisk,好像可以按照内核模块的方式加载到内核。

论坛徽章:
1
双子座
日期:2013-10-30 14:48:40
18 [报告]
发表于 2009-05-23 19:17 |只看该作者
mkfs.gt  gt_fs.h 这两个文件都是放在/include/linux下吗?
mkfs.gt中的具体内容什么?看不到。

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

回复 #17 Godbach 的帖子

我去搜搜看

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

回复 #18 flike 的帖子

gt_fs.h需要放到include/linux下
mkfs.gt是个二进制文件,你可以把它放到任意位置,只支持mkfs.gt /dev/sdax
需要代码的话我可以贴上来
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP