202302文件系统VFS阅读输出

168 阅读4分钟

缘起

  • 《Linux内核探秘 深入解析文件系统和设备驱动的架构与设计》书中文件系统的读书笔记,也网上找了些内容。
  • 配合看了另一本《存储技术原理分析:基于Linux_2.6内核源代码》的chap08
  • 本质上这算是VFS的相关内容,一些讲内核的书(从挂载、打开、读、写、IO层讲得可以),mapper device是单独讲的

内容

chap2、文件系统

2.3、从代码层次深入分析文件系统

2.3.3、文件系统的挂载过程
  • do_mount首先获得挂载点目录的dentry结构以及目的文件系统的vfsmount结构,这些信息保存在一个nameidata结构中
  • sys_mount()->do_mount()->do_new_mount()【这个过程中做了一些事情do_kern_mount()->vfs_kern_mount()->ext2_get_sb()->get_sb_bdve()->ex2_fill_super()】->do_add_mount()->graft_tree()->attach_recursive_mnt()->commit_tree()
  • 解决了我的一个疑问get_sb()对应的问题,原来有3种get_sb()的实现
    • int get_sb_bdev()
    • int get_sb_nodev()
    • int get_sb_single()
    • 以及 伪文件系统 int get_sb_pseudo()
2.3.4、文件打开的代码分析
  • 这个分析,涉及到了路径查找

  • //这个其实是路径查找
    sys_open() ->do_sys_open()->do_filp_open()->open_namei()->path_lookup_create()->__path_lookup_intent_open()->do_path_lookup()->link_path_walk()->__link_path_walk()->do_lookup()->__follow_mount()
    + __link_path_walk举例
    
  • open_namei()整个处理过程:得到了文件的dentry和inode结构,以及vfsmount对象。

  • sys_open() ->do_sys_open()->do_filp_open()->open_namei()->nameidata_to_filp()->__dentry_open(),这个是打开

chap10、文件系统读写

0、自行总结

  • page cache只有很重要的数据结构struct address_space{};

10.4、文件读过程代码分析

  • sys_read()->vfs_read()->如果file_operations里定义了就走自定义的,不然走do_sync_read().
  • 以ext2定义的generic_file_read()为例->__generic_file_aio_read()->do_generic_file_read()->do_generic_mapping_read()【这个是重点】
  • 从硬盘读数据是通过文件系统提供的readpage()实现的,ext2_readpage()->mpage_readpage()->do_mpage_readpage()->block_read_full_page()

10.5、读过程返回

  • 通过mpage_bio_submit()提交了一个I/O即调用->submit_bio(),啥时候返回?用什么机制通知上层?【书中的第11章,通用块层再讲】

10.6、文件写过程代码分析

  • ext2提供的是generic_file_write()->__generic_file_write_nolock()->__generic_file_aio_write_nolock()->generic_file_buffered_write()
  • 要写入硬盘,必须要获得文件块的物理块号,ext2_prepare_write()generic_commit_write()
    • ext2_prepare_write()->block_prepare_write()->__block_prepare_write()`
    • generic_commit_write()->__block_commit_write()

chap11、通用块层和SCSI层

0、自行总结(书上倒过来讲了)

  • 通用块层(Genric Block Layer),分2个部分【对应书中11.5节】

    • BIO层(block_io),跟gendisk有关,就是 块设备 相关
    • Request层
  • I/O调度【对应书中11.4】

    • noop
    • deadline
    • 还有个cfg(书中没讲)
  • SCSI:小型计算机系统接口(Small Computer System Interface)

    • 入口是init_scsi()
  • 通用块层和scsi层 单独有2个章节 讲这个,chap04,chap05

11.2、块设备队列

  • scsi_alloc_queue()【drivers/scsi/scsi_lib.c】
  • 电梯算法struct elevator_type elevator_noop{};

11.3、硬盘HBA抽象层

  • struct scsi_host_template{};

11.4、I/O的顺序控制

11.5、I/O调度算法

11.5.1、noop调度算法
  • noop_add_request()【block/deadline-iosched.c】
  • noop_dispatch()【block/deadline-iosched.c】
11.5.2、deadline调度算法
  • struct deadline_data{};
  • deadline_add_request();【block/deadline-iosched.c】

11.6、I/O处理过程

11.6.1、I/O插入队列
  • 文件系统提交I/O都要通过submit_io来提交一个I/O,lionel,这个我没看到嘛。

  • submit_bio()【block/ll_rw_blk.c】-> 文件系统VFS层和通用块层的衔接点

    • generic_make_request()
      • 第一部分:检查最大扇区限制
      • 第二部分:检查I/O的大小不应该超过块设备的最大可处理扇区
      • make_request_fn()就是__make_request()【block/ll_rw_blk.c】,判断I/O能否合并
        • 第一部分:从bio结构获得I/O的起始扇区地址和以扇区度量的I/O大小
        • 第二部分:检查是否需要bounce
        • 第三部分:处理后向合并
        • 第四部分:处理前向合并
        • 第五部分:申请请求
        • elv_merge()
          • __elv_add_request()【block/elevator.c】,lionel,我是没搞懂,怎么就变成了调用后面的函数了
            • elv_insert()
11.6.2、出队列
11.6.3、I/O返回路径

chap12、内核回写机制

  • 文件回写 fs/fs-writeback.c中的bdi_writeback_task()代码

  • 写磁盘pdflush(page dirty flush)线程完成,这是2.6的,之前是bdflush、kupdated两个线程,再后来被Flusher线程取代。

12.1、内核的触发条件

  • 文件系统的写函数generic_file_buffered_write()要调用balance_dirty_pages_ratelimited()
  • 内核定时器触发
  • 当系统申请内存失败,或者文件系统执行同步(sync)操作,或者内存管理模块试图释放更多内存的时候,都可能触发pdflush线程回写页面

12.2、内核回写控制参数

  • /proc/sys/vm
    • dirty_ratio,脏页比例,page-writeback.c
    • dirty_backgroud_ratio,背景脏页比例,page-writeback.c
    • dirty_writeback_centisecs,写延时,page-writeback.c
    • dirty_expire_centisecs,最大I/O延时,page-writeback.c

12.3、定时器触发回写

  • start_kernel()启动了一个定时器,推动内核回写数据
12.3.1、启动定时器
  • 调用page_writeback_init()函数启动定时器
12.3.2、执行回写操作
  • wb_kupdate()内核回写的控制函数
12.3.3、检查需要回写的页面
  • writeback_inodes()扫描系统的超级块,检查需要回写的页面
12.3.4、回写超级块内的inode
  • sync_sb_inodes()
    • 第一部分:遍历超级块链表的脏inode结构,检查是否可回写
    • 第二部分:首先检查时间参数
    • 3、检查inode状态
    • 4、文件回写操作,_sync_single_inode()

12.4、平衡写

  • balance_dirty_pages_ratelimited()
12.4.1、检查直接回写的条件
12.4.2、回写系统脏页面的条件
  • 计算dirty_thresh的限制
12.4.3、检查计算机模式
  • 笔记本模式
  • 普通模式

12.5、本章小结

  • Linux内核的回写机制提供了一种缓写机制,真实的写并不是直接写入硬盘,而是在page cache中缓存,等待合适的时机才真正执行写入。

最后

  • 大概看了个架构,也跟踪了代码,但细节没太get到,主要还是开始水平有限
  • 这算是2023-02月份的作业,下个月看ext2-3的源代码(ext4还没想好,是不是看,但会记住下理论的差异,再顺便复习这部分内容)