-----------------------------------写在2017/05/29 13:56--------------------------------------------------------
周六答辩,最近无所事事,重新看以前自己写的笔记,心中颇有感慨,写上来吧,如果哪天笔记本丢了,所有的回忆也都没有了。其实我真的不是一个会写东西的人,学习过程中也很少写,因此遇到同样的问题时总是又要重新百度,Google,这其实不是一个很好的习惯。
声明:因为很久没有看了,所以可能有一些是错误的,有疑问的小伙伴可以提出问题,我看到的话会回答的哦。毕竟我也是在学习中的嘛。
其实,如果你们看到这里,我相信,你们应该是一个热爱学习充满好奇心的人,笔记基本都是关于EXT2的,EXT4是研三下学期才看的,EXT4相对于EXT2来说差别很大!!但是EXT2是基础,看源代码比较好理解。好啦废话不多说啦!开始咯!
-----------------------------------------------------关于文件系统部分的源代码我都有注释,可以去我的github 下载,对你有用的话,可以给我一个star 哦 哈哈谢谢------------------
-----------------------------------github地址:https://github.com/Hafy/linux-2.6.32.67- ------------------------------------------------------------------
-----------------------------------------------转载请注明出处--------------------------------------------------------
1、分配一个struct ext2_sb_info * sbi结构
2、因为不知道该设备的块大小是多少,因此不知道超级块所在的块号以及偏移量是多少。因此会有一个猜测,然后计算得到一个逻辑上的猜测的超级块所在的块号logic_sb_block
(一)sb_bread函数解析
调用sb_bread(sb, logic_sb_block)获得该逻辑块号所在的缓冲区首部 即buffer_head 下面我们来分析一下sb_bread函数
最终会调用struct buffer_head *__bread(struct block_device *bdev, sector_t block, unsigned size)
{
struct buffer_head *bh = __getblk(bdev, block, size);
if (likely(bh) && !buffer_uptodate(bh))//buffer_uptodate(bh)==1表示该缓冲区存在有效数据 =0表示该缓冲区不存在有效数据
bh = __bread_slow(bh);//感觉是将磁盘上的相应的数据块读到上面的bh->b_data中
return bh;
}
分析struct buffer_head *__getblk(struct block_device *bdev, sector_t block, unsigned size)
{
struct buffer_head *bh = __find_get_block(bdev, block, size);
might_sleep();
if (bh == NULL)
bh = __getblk_slow(bdev, block, size);//页高速缓存中找不到该页则创建一页
return bh;
}
分析struct buffer_head *__find_get_block(struct block_device *bdev, sector_t block, unsigned size)
{
struct buffer_head *bh = lookup_bh_lru(bdev, block, size);//该函数在lru链表 里找我们要的缓冲区首部
if (bh == NULL) {
bh = __find_get_block_slow(bdev, block);//找不到调用该函数在页高速缓冲区查找buffer head
if (bh)
bh_lru_install(bh);//将我们在页高速缓冲区中找到的该逻辑块对应的buffer head结构加入到lru链表里面
}
if (bh)
touch_buffer(bh);
return bh;
}
关于__find_get_block总结:
在lru->bhs[]数组中查找是否有目标块对应的bh(lru->bhs实际上是最近最常访问的bh的一个数组),如果找到,则将该block对应的bh移到lru->bhs[0]中,数组越前面表示是最新的访问的bh
如果在lru中没有找到,则调用bh = __find_get_block_slow(bdev, block);//找不到调用该函数在页高速缓冲区查找buffer head,如果在页高速缓冲区中找到目标block对应的bh,则调用bh_lru_install(bh);//将我们在页高速缓冲区中找到的该逻辑块对应的buffer head结构加入到lru链表里面
如果在lru中与页高速缓冲区中都没有找到目标block对应的bh,则__find_get_block函数返回NULL.且调用_getblk_slow函数在页高速缓冲区中新建一个bh
分析static struct buffer_head *__getblk_slow(struct block_device *bdev, sector_t block, int size)
该函数最终调用static int grow_buffers(struct block_device *bdev, sector_t block, int size)
(1)通过block目标块号与块大小size计算该块对应的页的index
(按照我的理解是该index应该是该块号所在的页索引号,比如block 为 0,1,2,3 则index=0,block为4,5,6,7该index=1,假如块大小是1024的话,一个page可以包含4个block)
(2)到这一步时,block是形参的目标块号block对应的page的那四个block号的首个block号,也就是该page的偏移量为0的那个逻辑块号,比如如果index=1,则该该block=4
调用 page = grow_dev_page(bdev, block, index, size);
分析该函数:
① 在页高速中page = find_or_create_page(inode->i_mapping, index,(mapping_gfp_mask(inode->i_mapping) & ~__GFP_FS)|__GFP_MOVABLE);新建一个page
② bh = alloc_page_buffers(page, size, 0);//分配4个buffer_head 连成一个链表,最后返回链表头的buffer_head
每个bh->b_this_page指向下一个bh,且着四个bh=>b_data分别指向page->virtual ,page->virtual+1024,page->virtual+2018,page->virtual+3072 其实就是着四个bh对应 的page的虚拟地址,着四个bh的虚拟地址其实是连续的。
③link_dev_buffers(page, bh);//将buffer_head连成循环链表 ,将最后一个bh->b_this_page指回首个bh形成循环链表
④ init_page_buffers(page, bdev, block, size);//将和块号block在同一页的块号存进对应的bh->b_blocknr中
⑤成功返回 page
-----------------------------------sb_bread函数解析完了,继续--------------------------------
3、sbi->s_group_desc 是一个struct buffer_head ** s_group_desc; 接着刚刚的sb_bread
计算得到该设备的块组描述符占据的块数目db_count后
//db_count是存储该文件系统的全部块组描述符需要的数据块数
//假设需要10块 循环10次将这10个数据块在页高速缓存中的对应的buffer_head对应的挂接到
//ext2_sb_info->s_group_desc[]数组中
--------------------------------------------别的我就不说了,主要就是填充超级块的结构。。额 感觉自己理解的不是很充分,因此写的也乱七八糟。。。。---------------------------
----------edited by Haifei----------------------------------
-----------------29/06/2018--------------------
4、接上面,将磁盘的块组描述符信息读取到内存中后,调用ext4_check_descriptors函数对块组描述符进行检查,其实这个检查比较简单,就是check 每个desc存储的block bitmap,inode bitmap, inode table 号是否在该块组的first block和last block之间
5、ext4_load_journal 如果有journal inode 那么将其加载到内存中。
(/*
* The first inode we look at is the journal inode. Don't try1、调用ext4_iget 获取日志索引节点的struct inode内存结构
1)调用ext4_raw_inode 读取磁盘的日志inode数据到内存中
2)调用ext4_inode_blocks 从inode中获取日志节点描述的文件的总的块的数目,调用ext4_isize 获取日志文件的大小(字节)其实就是想知道整个日志文件的大小吧
3)接着是对该inode进行一些verify 具体我就不仔细看了。
2、jbd2_journal_init_inode 函数
jbd2_journal_init_inode creates a journal which maps an on-disk inode as
* the journal. The inode must exist already, must support bmap() and
* must have all data blocks preallocated
创建一个将磁盘inode映射为日志的日志。
主要调用jbd2_journal_bmap(journal, 0, &blocknr) ,这个函数大致是将blocknr 指向了日志文件在内存中的首个块,其实就是日志超级块。是做映射嘛?(不太确定,我对日志这部分不熟)
再调用getblk_unmovable函数从磁盘中读取日志超级块
调用ext4_init_journal_params函数初始化日志的一些params
6、调用ext4_iget(sb, EXT4_ROOT_INO); 获取根inode的信息,与上面流程类似。
/*
* The jbd2_journal_load will have done any necessary log recovery,*/
7、d_make_root 给根inode分配 根dentry
主要调用 static const struct qstr name = QSTR_INIT("/", 1);
res = __d_alloc(root_inode->i_sb, &name);
1)__d_alloc
先调用kmem_cache_alloc 在dentry cache中分配一个dentry,然后初始化dentry的各个字段
2)d_instantiate
将root_inode结构与root dentry结构关联起来
8、ext4_set_resv_clusters(sb); 待定
9、err = ext4_setup_system_zone(sb); 待定
吐血了,ext4_fill_super 比ext2_fill_super 多了这么多东西。。。