0%

Linux 0.11: File System

文件系统维护的数据结构:

  • inode_table 维护所有打开的 inode (m_inode), 一共 32 项
    in fs/inode.c
  • file_table: (NR_FILR, 8192 项)
    in fs/file_table.c
  • 每个进程的 task_struct 有一个成员是 filp (NR_OPEN, 20 项)记录该所有打开的文件
  • super_block: 全局超级块 (NR_SUPER, 8 项)
    in fs/super.c
    • s_imap: 指向 inode 位图的 buffer_head
    • s_zmap: 指向 i_zone 的 buffer_head

inode api:

  • iget() 用于打开一个 inode (引用计数 + 1), 先到 inode_table 中查找是否已打开,已打开则直接使用
  • iput() 用于释放一个 inode

安装根文件系统: mount_root()

in fs/super.c

由进程 1 在 sys_setup 中调用安装

  1. file_table 中所有文件的引用计数(f_count)清零
  2. 初始化 super_block 中所有超级块
    1
    2
    3
    p->s_dev = 0;
    p->s_lock = 0;
    p->s_wait = NULL;
  3. 读取根设备的超级块:read_super(ROOT_DEV)
  4. 读取根 inode : iget(ROOT_DEV,ROOT_INO)
    • 根 inode 号(ROOT_INO)为 1 (0 号 inode 不使用,表示未找到)
    • 并将引用计数 + 3 :之后由 super_block, current->pwd, current->root 使用
  5. 设置根设备的超级块的根 inode 号:p->s_isup = p->s_imount = mi
  6. 设置当前进程的 pwdroot 的 inode 号: current->pwd, current->root

读取超级块: read_super

  1. 检查参数
    • 检查设备号是否为 0
    • 检查磁盘是否更换(针对软盘):check_disk_change(dev);
  2. 根据设备号从 super_block 中获取超级块号 :get_super(dev)
    • 有已缓存的超级块则直接返回缓存的超级块
  3. 如果没有找到已存在的超级块,则从 super_block 中查找空闲的项(!s->dev)
    • 如果还是没有找到,返回 NULL
  4. 初始化找到的 super_block 项(在内存中的成员变量)并加锁:lock_super()
  5. bread 读取超级块到缓冲块中 (超级块在设备中的块号为 1)
    • 如果未读取到超级块,释放相应的 super_block 项并解锁: free_super()
  6. 将读取到的超级块的内容(char*) 赋值到 super_block 项的硬盘内容部分 (struct d_super_block *) ,在结构体的低地址处
  7. 检查读取到的超级块的 magic number
    • 如果 magic number 未匹配则认为文件系统格式错误,释放相应的 super_block 项并解锁: free_super()
  8. 读取超级块下的所有 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 的标识
  9. 释放超级块对应的 super_block 项

从 super_block 中找到已存在的 super_block 指针: get_super()

  1. 检查设备号
  2. 遍历检查 super_block ,检查每一个项的设备号是否和参数设备号一致
  3. 如果没找到,则返回 NULL
  4. 如果找到之后,等待锁的释放与磁盘同步:wait_on_super(), 之后再次检查并返回
    • 类似于 wait_on_buffer

inode_table 中获取 inode : iget


inode_table 找到对应的 inode 指针

in fs/inode.c

  1. 首先获取一个空闲 inode: get_empty_inode()
  2. 然后在 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 项
  3. 如果在现在的 inode_table 中未找到,则将最开始找到的空闲的 inode 作为要获取的 inode
    • 绑定该空闲 inode 的设备号和 inode 号
    • 然后从磁盘中读取 inode: read_inode()

从 inode_table 中查找一个空闲的 m_inode 项: get_empty_inode

  1. 使用 Next Fit 循环首次适应算法在 inode_table 中寻找 not dirt 并且没有 lock 的 inode
  2. 检查 inode 是否为空
  3. wait_on_inode(inode)
  4. 如果该 inode 是 dirt ,则将 inode 写入磁盘

从磁盘读取 d_inode 到 m_inode 项: read_inode

  1. 给 m_inode 加锁: lock_inode
  2. 查找当前设备的设备号: get_super(inode->i_dev)
  3. 计算 inode 的块号:2 + sb->s_imap_blocks + sb->s_zmap_blocks + (inode->i_num-1)/INODES_PER_BLOCK;
    • 引导块 + 超级块 + imap 块数 + zmap 块数 + inode 在所有 inode 块内的偏移块数
  4. bread() 将 inode 块读入缓冲区
  5. 将 d_inode 从缓冲区数据中 (根据块内偏移选择) 读取到 m_inode 项中
  6. 读取缓冲区完毕,释放缓冲区: brelse()
  7. 给 m_inode 解锁: unlock_inode

释放 inode_table 中的 inode : iput

  1. 先等待锁的释放与磁盘同步: wait_on_inode
  2. 检查引用计数不允许为 0
  3. 如果 i_pipe 为 1: …
  4. 如果当前 inode 未绑定设备 (inode 设备号为 0)
    • 可能的情况是:get_empty_inode 获取的空闲 inode, 未绑定任何设备
  5. 对于块设备文件的 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 时缓冲块可能被其他线程再次写入