一个简单文件系统的实现
时间:2009-05-23
来源:互联网
author goter
email [email protected]
*/
花了将进两个月的时候阅读完内核文件系统,对于文件系统是如何运行的还是有点模糊,所以想通过写一个简单的文件系统来使自己对文件系统有个深入的了解。经过拷贝抄袭ext2和minix文件系统后,写了一个简单的文件系统,我把这个简单的文件系统叫作GTFS,估计还有很多BUG,欢迎大家修正这些BUG
GTFS不支持磁盘配额和acl。链接,读写,删除,新建,改名等都支持,有时候会有莫名其妙的错误,希望大家谅解
下面先附上代码和使用信息
使用信息
在fs/Makefile中添加一行
- obj-$(CONFIG_GT_FS) += gt/
在fs/Kconfig中添加
- config GT_FS
- tristate "GOTER fs support"
- help
- GTFS is a simple Linux file system for hard disk.
在include/linux/Kbuild中添加一行
- unifdef-y += gt_fs.h
在include/linux/magic.h中添加一行
- #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,然后编译即可

我会在下面为大家分析GTFS代码的
[ 本帖最后由 goter 于 2009-5-23 20:17 编辑 ]
作者: goter 发布时间: 2009-05-23
下面是GTFS的主要数据结构
gt_inode(索引节点,在gt_fs.h中定义)
- 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=i_end_blocks-i_start_blocks+1
gt_inode_info(内存中的i节点信息,在gt.h中定义)
- 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;
- };
- 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中定义)
- struct gt_sb_info{
- struct gt_super_block * s_gs;//指向GT文件系统的超级块
- struct buffer_head * s_sbh;//指向超级块所在的缓冲区
- };
- #define GT_NAME_LEN 60 //目录项名字长度
- struct gt_dir_entry {
- __le32 ino;
- char name[GT_NAME_LEN];
- };
作者: Godbach: 发布时间: 2009-05-23
我们就先从挂载一个文件系统开始
挂载文件系统时候,VFS会通过mount命令中的文件系统类型或超级块魔数来寻找file_system_type结构
下面就是GTFS的file_system_type结构(定义在super.c中)
- 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,
- };
- 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_fill_super函数才是正真用来读取超级块的方法
- 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=>_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;
- }
调用gt_iget函数读入指定文件系统的根目录。
作者: goter 发布时间: 2009-05-23
也有module_init和module_exit(定义在super.c)
- module_init(init_gt_fs)
- module_exit(exit_gt_fs)
- static int __init init_gt_fs(void){
- int err=init_inodecache();
- if(err)
- return err;
- err=register_filesystem(>_fs_type);//向内核注册GT文件系统
- if(err)
- goto out;
- return 0;
- out:
- destroy_inodecache();
- return err;
- }
init_inodecache(定义在super.c中)
- 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;
- }
- static void __exit exit_gt_fs(void){
- unregister_filesystem(>_fs_type);//注销文件系统
- destroy_inodecache();//销毁缓冲池
- }
[ 本帖最后由 goter 于 2009-5-23 14:49 编辑 ]
作者: goter 发布时间: 2009-05-23
- 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中)
- 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;
- }
- static void gt_destroy_inode(struct inode *inode){
- kmem_cache_free(gt_inode_cachep,GT_I(inode));
- }
GT_I是定义在gt.h中的一个inline函数,根据VFS索引节点返回GTFS的内存索引节点
- static inline struct gt_inode_info *GT_I(struct inode *inode){
- return container_of(inode,struct gt_inode_info,vfs_inode);
- }
- static int gt_write_inode(struct inode *inode,int wait){
- brelse(gt_update_inode(inode));
- return 0;
- }
gt_update_inode(定义在inode.c中)
- 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_delete_inode(定义在super.c中)
- 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);//释放索引节点
- }
- 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中)
- 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索引节点
- }
作者: goter 发布时间: 2009-05-23
接下来就是gt_write_super(定义在super.c中)
- 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中
- 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;//返回未使用的索引节点数
- }
- 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;
- }
作者: goter 发布时间: 2009-05-23
- 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中)
- 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默认的函数
- const struct inode_operations gt_file_inode_operations ={
- .truncate =gt_truncate,
- };
[ 本帖最后由 goter 于 2009-5-23 15:03 编辑 ]
作者: goter 发布时间: 2009-05-23
- 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中定义)
- 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中定义)
- 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;
- }
[ 本帖最后由 goter 于 2009-5-23 15:09 编辑 ]
作者: goter 发布时间: 2009-05-23


作者: goter 发布时间: 2009-05-23
作者: goter 发布时间: 2009-05-23

作者: Godbach 发布时间: 2009-05-23
- 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文件系统对文件的读写是以页为单位的。
- 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中定义)
- 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中定义)
- 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中定义)
- 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中定义)
- static inline void *gt_next_entry(void *de){
- return (void *)((char *)de+sizeof(struct gt_dir_entry));
- }
这个函数也很简单,主用用来遍历目录项
gt_sync_file在前边已经分析完
这样目录操作也分析完了,下边是地址空间操作gt_aops
作者: tastesweet 发布时间: 2009-05-23
gt_aops(在inode.c中定义)
- 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中定义实现
- 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);
- }
地址空间操作就是这样,接下来是对目录索引节点的操作
作者: goter 发布时间: 2009-05-23
作者: goter 发布时间: 2009-05-23

作者: flike 发布时间: 2009-05-23
作者: goter 发布时间: 2009-05-23
需要重新编译内核,版本的话我是用的2.6.27.8,相近的版本应该也可以
需要重新编译内核啊。之前论坛上有另外一个网友上传了一个比较简单的Ramdisk,好像可以按照内核模块的方式加载到内核。
作者: goter 发布时间: 2009-05-23
mkfs.gt中的具体内容什么?看不到。
作者: Godbach 发布时间: 2009-05-23


作者: flike 发布时间: 2009-05-23
mkfs.gt是个二进制文件,你可以把它放到任意位置,只支持mkfs.gt /dev/sdax
需要代码的话我可以贴上来


作者: goter 发布时间: 2009-05-23
作者: goter 发布时间: 2009-05-23
。。
作者: flike 发布时间: 2009-05-23
哈哈
对内核不是很熟悉哈
难道 文件系统也是模块化编程里的一种?
呵呵 新手
作者: zenglei421 发布时间: 2009-05-23
学习下你的经验.
作者: zenglei421 发布时间: 2009-05-23
调试的话是按albcamus老大写的教程来的,很好用,地址http://linux.chinaunix.net/bbs/v ... p%3Bfilter%3Ddigest
作者: flike 发布时间: 2009-05-23
在namei.c中定义
- const struct inode_operations gt_dir_inode_operations={
- .create =gt_create,
- .lookup =gt_lookup,
- .link =gt_link,
- .unlink =gt_unlink,
- .symlink =gt_symlink,
- .mkdir =gt_mkdir,
- .rmdir =gt_rmdir,
- .mknod =gt_mknod,
- .rename =gt_rename,
- };
先来看gt_create(在namei.c中定义)
- static int gt_create(struct inode *dir,struct dentry *dentry,int mode,struct nameidata *nd){
- return gt_mknod(dir,dentry,mode,0);
- }
gt_create调用gt_mknod在dir中创建一个文件
gt_mknod(在namei.c中定义)
- static int gt_mknod(struct inode *dir,struct dentry *dentry,int mode,dev_t rdev){
- int error;
- struct inode *inode;
- if(!old_valid_dev(rdev))
- return -EINVAL;
-
- inode=gt_new_inode(dir,&error);//在dir代表的目录中新建一个索引节点
- if(inode){
- inode->i_mode=mode;
- gt_set_inode(inode,rdev);//根据素引节点类型,设置不同的操作
-
- mark_inode_dirty(inode);
- error=add_nondir(dentry,inode);//将目录项和索引节点联系起来
- }
- return error;
- }
这个函数调用了gt_new_inode,gt_set_inode,add_nondir这三个函数
先来看gt_new_inode(在inode.c中定义)
- struct inode *gt_new_inode(struct inode *dir,int *err){
-
- struct super_block *sb;
- struct buffer_head *bh;
- ino_t ino=0;
- int block;
- struct inode *inode;
-
- struct gt_inode_info *gi;
- struct gt_inode *raw_inode;
- char *p;
-
- sb=dir->i_sb;
- inode=new_inode(sb);//调用VFS函数,VFS函数又会调用前面分析过的超级块操作中的gt_alloc_inode函数来在内存中分配一个索引节点
- if(!inode)
- return ERR_PTR(-ENOMEM);
- gi=GT_I(inode);//获取到内存索引节点
-
- inode->i_uid=current->fsuid;
- inode->i_gid=(dir->i_mode&S_ISGID)?dir->i_gid:current->fsgid;
- inode->i_mtime=inode->i_atime=inode->i_ctime=CURRENT_TIME_SEC;
-
- block=2;//索引节点表所在的块
- struct gt_inode *prev=NULL;
- while(bh=sb_bread(sb,block)){//遍历索引节点表
- p=bh->b_data;
- while(p<=(bh->b_data+GT_BLOCK_SIZE-GT_INODE_SIZE)){
- raw_inode=(struct gt_inode *)p;
- ino++;
- if(!raw_inode->i_nlinks&&!raw_inode->i_start_block){//找到一个链接数为0,而且,开始块为0的磁盘索引节点
- if(!prev->i_reserved)//如果前一个索引节点的i_reserved为0,则设置其为默认的GT_BLOCK_RESERVED
- prev->i_reserved=GT_BLOCK_RESERVED;
- prev->i_blocks=prev->i_end_block-prev->i_start_block+1;//并且设置其i_blocks,表示该索引节点已经封顶
-
- mark_buffer_dirty(bh);
- goto find;
- }
- p+=GT_INODE_SIZE;
-
- prev=raw_inode;
- }
- brelse(bh);
- if(block>GT_INODE_BLOCK_COUNT(sb))
- break;
- block++;
- }
-
- iput(inode);
- brelse(bh);
- *err=-ENOSPC;
- return NULL;
- find:
- inode->i_ino=ino;
-
- raw_inode->i_start_block=prev->i_end_block+prev->i_reserved+1;//新磁盘索引节点的开始块等于前一个索引节点的结束块加上预留块
- gi->i_reserved=raw_inode->i_reserved;//根据新磁盘索引节点来设置内存索引节点
- gi->i_start_block=gi->i_end_block=raw_inode->i_start_block;
- raw_inode->i_end_block=raw_inode->i_start_block;//新磁盘索引节点的开始块等于结束块,此时并不设置i_blocks,因为这个索引节点是新建的,不需要被“封顶”
- brelse(bh);
- insert_inode_hash(inode);
- mark_inode_dirty(inode);
- *err=0;
- return inode;
- }
然后就是gt_set_inode(在namei.c中定义)
- void gt_set_inode(struct inode *inode,dev_t rdev){
- if(S_ISREG(inode->i_mode)){
- inode->i_op=>_file_inode_operations;
- inode->i_fop=>_file_operations;
- inode->i_mapping->a_ops=>_aops;
- }else if(S_ISDIR(inode->i_mode)){
- inode->i_op=>_dir_inode_operations;
- inode->i_fop=>_dir_operations;
- inode->i_mapping->a_ops=>_aops;
- }else if(S_ISLNK(inode->i_mode)){
- inode->i_op=>_symlink_inode_operations;
- inode->i_mapping->a_ops=>_aops;
- }else
- init_special_inode(inode,inode->i_mode,rdev);
- }
这个函数函数很重要,它根据索引节点的属性,设置其对应的操作方法,从而将VFS的操作与GTFS的操作联系起来
add_nondir(在namei.c中定义)函数
- static int add_nondir(struct dentry *dentry,struct inode *inode){
-
- int err=gt_add_link(dentry,inode);
- if(!err){
- d_instantiate(dentry,inode);//成功的话,将dentry链入inode对应的目录项队列中(一个索引节点可以对应很多目录项)
- return 0;
- }
- inode_dec_link_count(inode);//如果出错,则该索引节点的链接数应该减一
- iput(inode);
- return err;
- }
这个函数又调用gt_add_link来将dentry对应的目录项加入父目录,然后设置目录项对应的索引节点号
下面就是gt_add_link
- int gt_add_link(struct dentry *dentry,struct inode *inode){
- struct inode *dir=dentry->d_parent->d_inode;//获取到父目录
- const char *name=dentry->d_name.name;//目录项名称
- int namelen=dentry->d_name.len;
-
- struct page *page=NULL;
- unsigned long npages=gt_dir_pages(dir);
- unsigned long n;
- char *kaddr,*p;
- int ino;
- struct gt_dir_entry *de;
- loff_t pos;
- int err;
- char *namx=NULL;
- __u32 inumber;
-
- for(n=0;n<=npages;n++){
- char *limit,*dir_end;
- page=gt_get_page(dir,n);
- err=PTR_ERR(page);
- if(IS_ERR(page))
- goto out;
- lock_page(page);
- kaddr=(char *)page_address(page);
- dir_end=kaddr+gt_last_byte(dir,n);//在页内未使用空间的地址
- limit=kaddr+PAGE_CACHE_SIZE-sizeof(struct gt_dir_entry);
- for(p=kaddr;p<=limit;p=gt_next_entry(p)){
- de=(struct gt_dir_entry *)p;
- namx=de->name;
- inumber=de->ino;
- if(p==dir_end){//如果走到最后一个页的目录项结尾,则在此处插入新的目录项
- goto got_it;
- }
- if(!inumber)//如果在中途某个目录项没有被使用,对应的索引节点号为0(被删除),则跳到got_it
- goto got_it;
-
- err=-EEXIST;
- if(namecompare(namelen,GT_NAME_LEN,name,namx))
- goto out_unlock;
- ino=de->ino;
- }
- unlock_page(page);
- gt_put_page(page);
- }
- BUG();
- return -EINVAL;
- got_it:
- //准备开始写,pos为页内偏移,在pos开始处写
- pos=(page->index>>PAGE_CACHE_SHIFT)+p-(char *)page_address(page);
- err=__gt_write_begin(NULL,page->mapping,pos,sizeof(struct gt_dir_entry),AOP_FLAG_UNINTERRUPTIBLE,&page,NULL);
- if(err)
- goto out_unlock;
- memcpy(namx,name,namelen);
- memset(namx+namelen,0,GT_NAME_LEN-namelen-4);// 将名字数组多余的空间清空
- de->ino=inode->i_ino;//参数inode用来设置目录项对应的索引节点号
- err=gt_commit_chunk(page,pos,sizeof(struct gt_dir_entry));//提交写
- dir->i_mtime=dir->i_ctime=CURRENT_TIME_SEC;
- mark_inode_dirty(dir);
- out_put:
- gt_put_page(page);
- out:
- return err;
- out_unlock:
- unlock_page(page);
- goto out_put;
- }
这个函数用来设置目录想的名称和对应的索引节点号,并且将目录项写入父目录的块里
作者: goter 发布时间: 2009-05-23
- static struct dentry *gt_lookup(struct inode *dir,struct dentry *dentry,struct nameidata *nd){
- struct inode *inode=NULL;
- ino_t ino=0;
- dentry->d_op=dir->i_sb->s_root->d_op;
- if(dentry->d_name.len>GT_NAME_LEN)
- return ERR_PTR(-ENAMETOOLONG);
- struct page *page;
-
- struct gt_dir_entry *de=gt_find_entry(dentry,&page);
- if(de){
- ino=de->ino;
- gt_put_page(page);
- }
- if(ino){
- inode=gt_iget(dir->i_sb,ino);
- if(IS_ERR(inode))
- return ERR_CAST(inode);
- }
- d_add(dentry,inode);
- return NULL;
- }
顾名思义,这个函数就是要在dir目录里寻找名为dentry->d_name.name的目录项,只不过这个寻找的过程由gt_find_entry完成,如果寻找到的话,则调用gt_iget,读入该目录项对应的索引节点。并且调用d_add函数将该目录项链入索引节点。
gt_find_entry(在dir.c中定义)
- struct gt_dir_entry * gt_find_entry(struct dentry *dentry,struct page **res_page){
- const char *name=dentry->d_name.name;
- int namelen=dentry->d_name.len;
- struct inode *dir=dentry->d_parent->d_inode;//父目录
-
- unsigned long n;
- unsigned long npages=gt_dir_pages(dir);
- struct page *page=NULL;
- char *p;
-
- char *namx;
- __u32 inumber;
- *res_page=NULL;
-
- for(n=0;n<npages;n++){
- char *kaddr,*limit;
- page=gt_get_page(dir,n); //获取到父目录代表文件在内存中的第n页
- if(IS_ERR(page))
- continue;
- kaddr=(char *)page_address(page);
- limit=kaddr+gt_last_byte(dir,n)-sizeof(struct gt_dir_entry);
- for(p=kaddr;p<=limit;p=gt_next_entry(p)){
- struct gt_dir_entry *de=(struct gt_dir_entry *)p;
- namx=de->name;
- inumber=de->ino;
- if(!inumber)//如果该目录项没有被使用,跳过
- continue;
- if(namecompare(namelen,GT_NAME_LEN,name,namx))//判断是否匹配
- goto found;//如果匹配则调到found处
- }
- gt_put_page(page);
- }
- return NULL;
- found:
- *res_page=page;//设置参数返回值
- return (struct gt_dir_entry *)p;
- }
这段代码和我们之前看过的代码很象,都是循环遍历,所以也没什么好讲的
接下来是
gt_iget(在inode.c中定义)
- struct inode *gt_iget(struct super_block *sb,unsigned long ino){
-
- struct gt_inode_info *gi;
- struct buffer_head *bh;
- struct gt_inode *raw_inode;
-
- long ret=-EIO;
-
- struct inode *inode=iget_locked(sb,ino);
-
- if(!inode)
- return ERR_PTR(-ENOMEM);
- if(!(inode->i_state &I_NEW))
- return inode;
- gi=GT_I(inode);
-
- raw_inode=gt_raw_inode(inode->i_sb,ino,&bh);
- if(!raw_inode){
- iput(inode);
- return NULL;
- }
-
- inode->i_mode=raw_inode->i_mode;
- inode->i_uid=(uid_t)raw_inode->i_uid;
- inode->i_gid=(gid_t)raw_inode->i_gid;
- inode->i_size=raw_inode->i_size;
- inode->i_nlink=raw_inode->i_nlinks;
- inode->i_rdev=raw_inode->i_dev;
- inode->i_mtime.tv_sec = inode->i_atime.tv_sec = inode->i_ctime.tv_sec = raw_inode->i_ctime;
- inode->i_mtime.tv_nsec = 0;
- inode->i_atime.tv_nsec = 0;
- inode->i_ctime.tv_nsec = 0;
-
- gi->i_dtime=raw_inode->i_dtime;
-
- if(inode->i_nlink==0 && (inode->i_mode==0||gi->i_dtime)){
-
- brelse(bh);
- ret=-ESTALE;
- goto bad_inode;
- }
-
- inode->i_blocks=raw_inode->i_blocks;
- gi->i_dtime=0;
- gi->i_start_block=raw_inode->i_start_block;
- gi->i_end_block=raw_inode->i_end_block;
- gi->i_blocks=raw_inode->i_blocks;
- gi->i_reserved=raw_inode->i_reserved;
-
- gt_set_inode(inode,inode->i_rdev);
- brelse(bh);
- unlock_new_inode(inode);
-
- return inode;
- bad_inode:
- iget_failed(inode);
- return ERR_PTR(ret);
- }
这个函数首先调用iget_locked来在内存中寻找超级块sb中索引节点号是ino的索引节点,如果找到就返回,如果找不到就在内存中分配一个inode结构,并且设置其索引节点号和状态,如果iget_locked返回的VFS索引节点的状态为新,说明这个VFS索引节点是在iget_locked中新分配的,需要从磁盘读入磁盘索引节点来填充VFS索引节点和内存索引节点,于是接着调用gt_raw_inode,对VFS索引节点和内存索引节点进行设置,于是,这个函数通过参数指定的超级块和索引节点号,返回在代表这个索引节点的VFS索引节点
[ 本帖最后由 goter 于 2009-5-24 08:40 编辑 ]
作者: goter 发布时间: 2009-05-24
作者: goter 发布时间: 2009-05-24
作者: flike 发布时间: 2009-05-24
作者: liuty2006 发布时间: 2009-05-25
热门阅读
-
office 2019专业增强版最新2021版激活秘钥/序列号/激活码推荐 附激活工具
阅读:74
-
如何安装mysql8.0
阅读:31
-
Word快速设置标题样式步骤详解
阅读:28
-
20+道必知必会的Vue面试题(附答案解析)
阅读:37
-
HTML如何制作表单
阅读:22
-
百词斩可以改天数吗?当然可以,4个步骤轻松修改天数!
阅读:31
-
ET文件格式和XLS格式文件之间如何转化?
阅读:24
-
react和vue的区别及优缺点是什么
阅读:121
-
支付宝人脸识别如何关闭?
阅读:21
-
腾讯微云怎么修改照片或视频备份路径?
阅读:28