Chinaunix首页 | 论坛 | 博客
  • 博客访问: 53032
  • 博文数量: 31
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 23
  • 用 户 组: 普通用户
  • 注册时间: 2013-09-01 11:00
文章分类
文章存档

2014年(24)

2013年(7)

我的朋友

分类: LINUX

2014-03-13 21:41:46

myfs_fill_super是一个比较重要的函数,用于在mount的过程中填充super_block,注意这儿myfs_fill_super的参数sb是上层已经分配好的一个super_block指针,我们只需要在这儿填充就可以了。

static int myfs_fill_super(struct super_block *sb,void *data, int silent)

{

         struct myfs_sb_info *sbi = NULL;

         struct dentry*root_dir;

         struct myfs_sb * myfs_sb;//每一个文件系统的超级块信息在物理介质上的保存方式都不一样,这个因设计者的想法而已。这边就需要讲到另外一个问题,就是在文件系统设计之初,如何考虑各个部分数据的分布问题。下面这个表格是myfs的数据分布

超级块

根目录下的子文件(myfs只支持一级目录)

数据分布

一个block

一个block

剩余block

Myfs暂时只实现了NAND部分,所以这边的block都是NANDblock,而不是块设备的block

         struct inode *root_inode = NULL;

         struct mtd_info *mtd;

         char devname_buf[33];

         int err = 0,retlen;

         unsigned int i,j ,k,total_blocks,total_inodes;

         struct myfs_inode * li;

         myfs_trace("%s",__func__);

         if (!sb)

             printk("myfs: sb is NULL\n");

         else if (!sb->s_dev)

        printk("myfs: sb->s_dev is NULL\n");

         else if (!bdevname(sb->s_bdev, devname_buf))

        printk("myfs: devname is NULL\n");

     /* Check it's an mtd device..... */

         if (MAJOR(sb->s_dev) != MTD_BLOCK_MAJOR)

                   return MYFS_FAIL;    /* This isn't an mtd device */

        sb->s_magic = MYFS_MAGIC1;//每一个文件系统都一个MAGIC_NUMBER ,并且是唯一的,用于却别与别的文件系统。

       sb->s_op = &myfs_super_ops;//超级块的操作结构体,后面再讲

       sb->s_flags |= MS_NOATIME;// MS_NOATIMEmount时候的一个参数,你也可以不知道,随便写都可以,读者可以通过man mount来看具体的含义。

#ifdef        CONFIG_IS_NAND

     /* Get the device */

    mtd = get_mtd_device(NULL, MINOR(sb->s_dev));

         if(!mtd){

        myfs_trace("MTD device #%u doesn't appear to exist",MINOR(sb->s_dev));

        return MYFS_FAIL;

    }

         myfs_trace("MTD name:%s",mtd->name);

         myfs_trace("MTD size:%llu",mtd->size);

         myfs_trace("MTD writesize:%u",mtd->writesize);

         myfs_trace("MTD erasesize:%u",mtd->erasesize);

         myfs_trace("MTD oobsize:%u",mtd->oobsize);

//做一些前期的校验,因为后面的空间分配,读写等等都依赖于这些的writesize,擦除依赖于erasesize的大小,为了防止将来出错,所以需要提前确保无误。

         if(!is_power_of_2(mtd->writesize)){

                   myfs_trace("invalid writesize");

                   goto fail;

         }

         if(!is_power_of_2(mtd->erasesize)){

                   myfs_trace("invalid erasesize");

                   goto fail;

         }

    /* Check it's NAND */

         if (mtd->type != MTD_NANDFLASH) {

                   myfs_trace("MTD device is not NAND it's type %d",mtd->type);

                   return MYFS_FAIL;

    }

    myfs_trace("erase %p", mtd->erase);

    myfs_trace("read %p", mtd->read);

    myfs_trace("write %p", mtd->write);

    myfs_trace("readoob %p", mtd->read_oob);

         /* Check for functions */

    if (!mtd->erase ||!mtd->read ||!mtd->write || !mtd->read_oob || !mtd->write_oob) {

                   myfs_trace("MTD device does not support required functions");

                   return MYFS_FAIL;

         }

#endif

         //好戏要开始了,怎么去填充superblock

         sbi = kzalloc(sizeof(struct myfs_sb_info), GFP_KERNEL);

         if(!sbi)

                   return -ENOMEM;

         sb->s_fs_info = sbi;

         //通常每一个文件系统都会对于VFS所规定的要求做以前扩充。比如EXT4可能用一个叫做EXT4_superblock的超类来兼容superblock,但是我们通常要保证,这两个结构体都能互相找到对方。因为说不定什么时候就用到了。

         //比如在超类myfs_superblock中设置一个指针指向 sb,这样可以通过sbi->sb来找到sb,同时又可以通过container_of(ino, struct myfs_inode_info, vfs_inode);sb找到sbi

         sbi->sb = sb;

         sb->s_blocksize = mtd->erasesize;

         myfs_sb = &sbi->myfs_sb;

#ifdef CONFIG_IS_NAND

         sbi->mtd = mtd;

         sbi->buf = kmalloc(mtd->writesize ,GFP_KERNEL);

         if(!sbi->buf){

                   err = -ENOMEM;

                   goto fail;

         }

         //分配一个buf,大小为mtd->erasesize,用于擦除时做数据的保存。后面会讲到。虽然myfs是一个NAND型的文件系统,但是并没有考虑到损耗均衡等等因素,要不然会变得很复杂。

         sbi->block_buf = kmalloc(mtd->erasesize ,GFP_KERNEL);

         if(!sbi->block_buf){

                   err = -ENOMEM;

                   goto fail;

         }

         myfs_trace("read superblock \n");

         /*

          *just read the first five pages to find the superblock

          *why need to read five pages ? Q:maybe bad page

          */

         for(i  = 0 ;i < mtd->erasesize/mtd->writesize;i++){

                   err = mtd->read(mtd,i*mtd->writesize,mtd->writesize,&retlen,sbi->buf);

                   if(!err)

                            break;

                   myfs_trace("read superblock failed %d, %d times",err,i);

                   mdelay(1000);

         }

#endif

         myfs_trace("read superblock ok");

         memcpy(myfs_sb,sbi->buf,sizeof(struct myfs_sb));

         if((myfs_sb->magic1 != MYFS_MAGIC1 ) ||(myfs_sb->magic2 != MYFS_MAGIC2)){

                   myfs_trace("invalid magic:%x ,%x \n",myfs_sb->magic1,myfs_sb->magic2);

                   /*format and write a new superblock*/

                   myfs_format(sbi,mtd,i);

         }

         if((myfs_sb->page_size != mtd->writesize ) || (myfs_sb->block_size != mtd->erasesize)){

                   myfs_trace("invalied pagesize or erasesize");

                   myfs_format(sbi,mtd,i);

         }

         if(myfs_sb->block_shift != (ffs(mtd->erasesize) - 1)){

                   myfs_trace("invalid block_shift");

                   myfs_format(sbi,mtd,i);

         }

         if(myfs_sb->page_shift != (ffs(mtd->writesize) - 1)){

                   myfs_trace("invalid page_shift");

                   myfs_format(sbi,mtd,i);

         }

         /*verify the sb_in_page*/

         if(myfs_sb->sb_in_page != i ){

                   myfs_trace("sb_in_page (%d) is not equal to %d",myfs_sb->sb_in_page,i);

                   myfs_sb->sb_in_page = i;

         }

         total_blocks = mtd->size >> (ffs(mtd->erasesize) - 1);

         //这儿在系统内部并没有保存文件系统的统计信息,比如哪些块被使用了,哪些块还是空闲的,而是在mount的过程中去扫描。所以通过一个block_bitmap_node的字段来动态的保存文件系统的统计信息。

         sbi->block_bitmap_node  = kmalloc((sizeof(struct block_bitmap_node) + (total_blocks+7)/8),GFP_KERNEL);

         if(!sbi->block_bitmap_node){

                   myfs_trace("kmalloc sbi->block_bitmap_node failed");

                   err = -ENOMEM;

                   goto fail;

         }

         sbi->block_bitmap_node->version = 0;

         sbi->block_bitmap_node->magic = BITMAP_MAGIC;

         sbi->block_bitmap_node->bitmap_size = (total_blocks+7)/8;//每一个bit代表一个块的使用情况

         memset(sbi->block_bitmap_node->block_bitmap,0x0,sbi->block_bitmap_node->bitmap_size);

         /*statistics the block status*/

         set_bit_to_bitmap(sbi->block_bitmap_node,0);                       /*superblock block*/

         set_bit_to_bitmap(sbi->block_bitmap_node,1);                       /*inode block*/

         myfs_sb->free_blocks = total_blocks - 2;

         total_inodes = MAX_INODE_NUMBERS;

         //后面会说到inode,这儿先提一下。Inode是文件的唯一标示,不管是文件还是文件夹还是其他。所以每一个inodenumber都是唯一的。所以分配的时候要小心。这儿现在mount的时候统计已经被使用掉的number号,以免后续的分配重复。

         sbi->inode_bitmap_node = kmalloc((sizeof(struct inode_bitmap_node) + (total_inodes+7)/8),GFP_KERNEL);

         if(!sbi->inode_bitmap_node){

                   myfs_trace("kmalloc sbi->inode_bitmap_node fail");

                   err = -ENOMEM;

                   goto fail;

         }

         sbi->inode_bitmap_node->version = 0;

         sbi->inode_bitmap_node->bitmap_size = (total_inodes+7)/8;

         memset(sbi->inode_bitmap_node->inode_bitmap,0x00,sbi->inode_bitmap_node->bitmap_size);

         /*set inode 0 is used*/

         sbi->inode_bitmap_node->inode_bitmap[MYFS_ROOT_INO/8] |= 1<<(MYFS_ROOT_INO%8);

         for(i = 0; i < sbi->block_bitmap_node->bitmap_size ; i++)

                   myfs_trace("0x%x",sbi->block_bitmap_node->block_bitmap[i]);

         myfs_trace("begin to statics block status, total blocks (%d)",total_blocks);

         //上面提到了分配第二个block用于存储文件信息,这儿读取这个blcok,做两方面的统计,一方面统计哪些inode number被占用了,另外一方面统计哪些block被使用了。

其实这个block中线性存储的都是struct myfs_inode类型的数据。

struct myfs_inode   {

         unsigned char flags;/*EMPTY,DELETE ,NORMAL INODES?*/

         unsigned char attr; /*file,directory ?*/

         unsigned char name[MYFS_MAX_NAME_LEN];//文件名称

         unsigned int inode_no;//节点号

         unsigned int inode_version;

         unsigned int filesize;//文件系统大小

         uid_t uid;

         gid_t gid;

         struct timespec  mod_time;

         struct timespec  creat_time;

         struct timespec  write_time;

         unsigned int assigned_block[MYFS_MAX_BLOCK_LEN];

         /*

          *if devices is nand ,first_cl[0-15] = page,first_cl[16:31] = block;

          *if devices is emmc ,first_cl = sector in partition

          */

};其中assigned_block[MYFS_MAX_BLOCK_LEN]用于存储该文件的数据存储的位置,所以在myfs中文件大小被限制在MYFS_MAX_BLOCK_LEN*mtd->erasesize

         for( i = 0 ; i < mtd->erasesize/mtd->writesize;i++){

                   err = mtd->read(mtd,mtd->erasesize + i*mtd->writesize,mtd->writesize,&retlen,sbi->buf);

                   if(err){

                            myfs_trace("%s,read failed ,err = %d ",__func__,err);

                            return -EIO;

                   }

                   li = (struct myfs_inode*)sbi->buf;

                   for(j = 0; j < mtd->writesize/sizeof(struct myfs_inode);j++,li++){

                            if(i == 0 && j == 0)//位置为0inode已经被根目录项占据了,所以跳过

                                     continue;

                            if(li->flags == MYFS_ATTR_FILE){

                                     //NAND中,空白读出的数据位0XFF,所以这儿的total_inodes <= 0XFF。如果inode_no介于 1 total_inodes,那么就是一个有效的inode number,然后就去设置相应的位图。

                                     if(li->inode_no >=1 &&li->inode_no <= total_inodes)

                                               /*set this inode number has been used*/

                                               sbi->inode_bitmap_node->inode_bitmap[li->inode_no/8] |= 1<<(li->inode_no%8);

                            }

                            //如果是一个有效的文件,那么查看文件所占用的数据块,然后去设置相应的bit为来表明数据块已经被使用了。

for(k = 0 ; k < MYFS_MAX_BLOCK_LEN;k++){

                                     if((li->assigned_block[k] == 0x0))

                                               break;

                                     if(li->assigned_block[k] >= total_blocks)

                                               continue;

                                     set_bit_to_bitmap(sbi->block_bitmap_node,li->assigned_block[k]);

                                     myfs_sb->free_blocks--;

                            }

                   }

         }

         myfs_trace("free_blocks %d",myfs_sb->free_blocks);

         myfs_trace("statics done");

         //单单有了superblock还不行,必须还有一个根目录。但是通常根目录都是胡诌出来的。因为根目录的信息想想都知道,inode number0,是个目录文件等等。所以大部分的文件系统都是在mount的过程中胡诌一个根目录出来。

         //注意这儿的new_inode函数,虽然是一个通用函数,但是它的参数为sb,因为它用到了超级块操作结构体中的一个函数。以后再讲。

         root_inode = new_inode(sb);

         if (!root_inode){

                   err = -ENOMEM;

         goto fail;

         }

         root_inode->i_ino =  MYFS_I(root_inode)->ino = MYFS_ROOT_INO;

         root_inode->i_version = 1;

         myfs_fill_root(sb,root_inode);

         myfs_trace("got root inode");

         //这儿又出来一个VFS中常用的结构体dentry,对于通常的文件,基本都是一个dentry,一个inode搞定。但是软连接和硬链接就不一样了。但是myfs中压根就没想过支持链接文件=.=!!

         root_dir = d_alloc_root(root_inode);

         if(!root_dir){

                   myfs_trace("d_alloc_root failed");

                   iput(root_inode);

                   err = MYFS_FAIL;

                   goto fail;

         }

         myfs_trace("d_alloc_root done");

         sbi->root = MYFS_I(root_inode);    

         sbi->lif = NULL;

         sb->s_root = root_dir;

         return 0;

fail:

         myfs_trace("%s failed",__func__);

         if(sbi->buf)

                   kfree(sbi->buf);

         if(root_inode)

                   iput(root_inode);

         sb->s_fs_info = NULL;

         if(sbi)

                   kfree(sbi);

         return err;       

}

至于超级块的操作结构体先贴上:

struct super_operations myfs_super_ops = {

    .alloc_inode    = myfs_alloc_inode,//分配私有inode节点

    .destroy_inode  = myfs_destroy_inode,//销毁私有inode节点

    .write_inode    = myfs_write_inode,//如何写入一个节点的相关信息到存储介质中去

     .evict_inode    = myfs_evict_inode,//如何从存储介质中删除一个节点

    .put_super      = myfs_put_super,

    .write_super    = myfs_write_super,//如何在sync或者unmount的时候写入superblock

    .sync_fs        = myfs_sync_fs,//同上

    .statfs         = myfs_statfs,//获得文件系统的相关信息

    .remount_fs     = myfs_remount,//如何remount文件系统,在myfs中直接return 0

};

这儿先说myfs_alloc_inode函数,因为上面的代码

         root_inode = new_inode(sb);

会调用到这个函数。

还是我们说到的VFS会有一个结构体inode,但是基本每一个具体的文件系统的具体XXX_inode都是VFSinode的超集。所以一般的具体的文件系统的inode数据结构都定义如下:

struct myfs_inode_info {

         struct inode vfs_inode;//包含struct inode

         ….

         struct myfs_inode myfs_inode;

         struct myfs_inode_info *next;

         struct myfs_inode_info *child;

         struct myfs_inode_info *parent;

         unsigned int ino;

         unsigned int dirty;

};

就如前面superblock所说的,可以互相找到对方。既然每一个具体文件系统的inode都不一样,那么需要一个函数来分配inode节点。这就是

static struct inode *myfs_alloc_inode(struct super_block *sb)

{

     struct myfs_inode_info *li = NULL;

          myfs_trace("myfs_alloc_inode");

     li = kzalloc(sizeof(struct myfs_inode_info),GFP_KERNEL);

     if(!li)

                 return NULL;

     li->myfs_inode.attr = MYFS_ATTR_FILE;

     li->myfs_inode.flags = MYFS_INODE_NORMAL;

     li->myfs_inode.inode_version = 0;

          inode_init_once(&li->vfs_inode);

          myfs_trace("myfs_alloc_inode - ok");

     return &li->vfs_inode;

}

static void(struct super_block *sb,struct inode* inode)

{

         inode->i_mode  &= ~S_IFMT;

         inode->i_mode  |= S_IFDIR;//根目录是一个目录,directory

    inode->i_op = &myfs_inode_operations;

    inode->i_fop = &myfs_dir_operations;

inode->i_mapping->a_ops = &myfs_file_address_operations;

//这儿不得不提到文件系统的另外三大操作结构体,inode操作结构体,dir目录操作结构体,以及address_space操作结构体

         inode->i_blocks = 0;

         inode->i_size = 0;

         inode->i_rdev = sb->s_dev;

         inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;

         inode->i_ino = MYFS_ROOT_INO;//0

         inode->i_uid = current->cred->fsuid;

         inode->i_gid = current->cred->fsgid;

         MYFS_I(inode)->myfs_inode.filesize = 0;

}

         mount的过程中需要虚构(或者从存储介质中读取)一个根目录,myfs_fill_root用于初始化这个根目录项的inode节点。

         另外不得不提的是,在写文件系统的过程中,strace命令尤为重要,可以让你准确的知道是哪一个接口函数出了问题。比如

         Strace mount –t myfs /dev/mtdblock3 /mnt

 

阅读(525) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~