文件系统维护的数据结构:
inode_table维护所有打开的inode(m_inode), 一共 32 项
infs/inode.cfile_table: (NR_FILR, 8192 项)
infs/file_table.c- 每个进程的
task_struct有一个成员是filp(NR_OPEN, 20 项)记录该所有打开的文件 super_block: 全局超级块 (NR_SUPER, 8 项)
infs/super.cs_imap: 指向 inode 位图的 buffer_heads_zmap: 指向 i_zone 的 buffer_head
inode api:
iget()用于打开一个 inode (引用计数 + 1), 先到inode_table中查找是否已打开,已打开则直接使用iput()用于释放一个 inode
安装根文件系统: mount_root()
in fs/super.c
由进程 1 在 sys_setup 中调用安装
- 将
file_table中所有文件的引用计数(f_count)清零 - 初始化
super_block中所有超级块1
2
3p->s_dev = 0;
p->s_lock = 0;
p->s_wait = NULL; - 读取根设备的超级块:
read_super(ROOT_DEV) - 读取根 inode :
iget(ROOT_DEV,ROOT_INO)- 根 inode 号(
ROOT_INO)为 1 (0 号 inode 不使用,表示未找到) - 并将引用计数 + 3 :之后由
super_block,current->pwd,current->root使用
- 根 inode 号(
- 设置根设备的超级块的根 inode 号:
p->s_isup = p->s_imount = mi - 设置当前进程的
pwd和root的 inode 号:current->pwd,current->root
读取超级块: read_super
- 检查参数
- 检查设备号是否为 0
- 检查磁盘是否更换(针对软盘):check_disk_change(dev);
- 根据设备号从
super_block中获取超级块号 :get_super(dev)- 有已缓存的超级块则直接返回缓存的超级块
- 如果没有找到已存在的超级块,则从
super_block中查找空闲的项(!s->dev)- 如果还是没有找到,返回 NULL
- 初始化找到的 super_block 项(在内存中的成员变量)并加锁:
lock_super() bread读取超级块到缓冲块中 (超级块在设备中的块号为 1)- 如果未读取到超级块,释放相应的 super_block 项并解锁:
free_super()
- 如果未读取到超级块,释放相应的 super_block 项并解锁:
- 将读取到的超级块的内容(
char*) 赋值到 super_block 项的硬盘内容部分 (struct d_super_block *) ,在结构体的低地址处 - 检查读取到的超级块的 magic number
- 如果 magic number 未匹配则认为文件系统格式错误,释放相应的 super_block 项并解锁:
free_super()
- 如果 magic number 未匹配则认为文件系统格式错误,释放相应的 super_block 项并解锁:
- 读取超级块下的所有 imap
- 首先清空 super_block 项的 s_imap 和 s_zmapnode
- 从 imap 对应的 block (2)开始,依次从磁盘中读取相应的块,将所有的 imap 和 i_zone 读入内存
- 如果读取的块数目和超级块中记录的块数目不一致,则释放所有刚刚读取的块和相应的 super_block 项并解锁:
free_super() - 将 super_block 项的 s_imap 和 s_zmap 中的第一个块对应的 bit 置 1: 表示不使用 0 号 inode 和 0 号块
- 是为了用 0 表示之后找不到 inode 的标识
- 释放超级块对应的 super_block 项
从 super_block 中找到已存在的 super_block 指针: get_super()
- 检查设备号
- 遍历检查 super_block ,检查每一个项的设备号是否和参数设备号一致
- 如果没找到,则返回 NULL
- 如果找到之后,等待锁的释放与磁盘同步:
wait_on_super(), 之后再次检查并返回- 类似于
wait_on_buffer
- 类似于
从 inode_table 中获取 inode : iget
in fs/inode.c
- 首先获取一个空闲 inode:
get_empty_inode() - 然后在 inode_table 中查找是否有已有的 inode: 检查设备号 dev 和 inode 号是否匹配
- 找到后,首先
wait_on_inode等待锁的释放与磁盘同步 - 然后再次检查设备号 dev 和 inode 号是否匹配 (防止其他线程在等待期间修改 inode)
- 此时确定 inode 已经找到,引用计数 i_count + 1
- 如果当前 inode 的 i_mount 为 1,表示当前的 inode 是其他文件系统的一个挂载点(即有一个文件系统挂载到了当前文件系统的该 inode 处)
- 首先在
super_block中查找哪一个文件系统的挂载点为该 inode (检查超级块的 s_imount 指针与该 inode 指针是否匹配), 未找到则出错,打印出错信息,释放之前获取的空闲 inode 并返回 - 找到超级块后,释放该 inode:
iput(inode) - 然后继续在 inode_table 中查找挂载的文件系统的超级块对应的设备号和根 inode 对应的 m_inode 项, 知道找到非挂载点的 inode
- 首先在
- 如果不是挂载点(i_mount 为 0),认为已经找到,则释放之前获取的空闲 inode 并返回找到的 m_inode 项
- 找到后,首先
- 如果在现在的 inode_table 中未找到,则将最开始找到的空闲的 inode 作为要获取的 inode
- 绑定该空闲 inode 的设备号和 inode 号
- 然后从磁盘中读取 inode:
read_inode()
从 inode_table 中查找一个空闲的 m_inode 项: get_empty_inode
- 使用 Next Fit 循环首次适应算法在 inode_table 中寻找 not dirt 并且没有 lock 的 inode
- 检查 inode 是否为空
wait_on_inode(inode)- 如果该 inode 是 dirt ,则将 inode 写入磁盘
从磁盘读取 d_inode 到 m_inode 项: read_inode
- 给 m_inode 加锁:
lock_inode - 查找当前设备的设备号:
get_super(inode->i_dev) - 计算 inode 的块号:
2 + sb->s_imap_blocks + sb->s_zmap_blocks + (inode->i_num-1)/INODES_PER_BLOCK;- 引导块 + 超级块 + imap 块数 + zmap 块数 + inode 在所有 inode 块内的偏移块数
bread()将 inode 块读入缓冲区- 将 d_inode 从缓冲区数据中 (根据块内偏移选择) 读取到 m_inode 项中
- 读取缓冲区完毕,释放缓冲区:
brelse() - 给 m_inode 解锁:
unlock_inode
释放 inode_table 中的 inode : iput
- 先等待锁的释放与磁盘同步:
wait_on_inode - 检查引用计数不允许为 0
- 如果 i_pipe 为 1: …
- 如果当前 inode 未绑定设备 (inode 设备号为 0)
- 可能的情况是:
get_empty_inode获取的空闲 inode, 未绑定任何设备
- 可能的情况是:
- 对于块设备文件的 inode:
S_ISBLK(inode->i_mode)- 同步整个设备:
sync_dev(inode->i_zone[0]),inode->i_zone[0]此时保存的是该块设备 inode 的块设备号 - 同步该 inode:
wait_on_inode(inode)
- 同步整个设备:
同步设备: sync_dev(int dev)
in fs/buffer.c
关机时经常会有该操作,因此不建议直接拔电源关机
- 遍历所有与当前设备关联的缓冲块, 同步所有脏块到磁盘
- 遍历 inode_table 中所有与当前设备关联的 inode, 同步所有脏的 inode 到磁盘:
sync_inodes() - 再次遍历所有与当前设备关联的缓冲块, 同步所有脏块到磁盘
- 同步 inode 时缓冲块可能被其他线程再次写入