- 作者: 陈孝松
- 主页: chenxiaosong.com
- 哔哩哔哩: 陈孝松
- 课程: chenxiaosong.com/courses
- 博客: chenxiaosong.com/blog
- 贡献: chenxiaosong.com/contributions
- 邮箱: chenxiaosong@chenxiaosong.com
- QQ交流群: 544216206, 点击查看群介绍
点击这里在哔哩哔哩bilibili在线观看配套的加餐视频(就是一些补充)。
一般的Linux书籍都是先讲解进程和内存相关的知识,但我想先讲解文件系统。第一,因为我就是做文件系统的,更擅长这一块,其他模块的内容我还要再去好好看看书,毕竟不能误人子弟嘛;第二,是因为文件系统模块更接近于用户态,是相对比较好理解的内容(当然想深入还是要下大功夫的),由文件系统入手比较适合初学者。
虚拟文件系统英文全称Virtual file system,缩写为VFS,又称为虚拟文件切换系统(virtual filesystem switch)。所有的文件系统都要先经过虚拟文件系统层,虚拟文件系统相当于制定了一套规则,如果你想写一个新的文件系统,只需要遵守这套规则就可以了。
VFS虽然是用C语言写的,但使用了面向对象的设计思路。
磁盘块可能不连续和动态变化的,文件访问需要将文件看作一个连续的字节流,这个矛盾的解决核心在于地址空间的引入。
// 可缓存、可映射对象的内容。
struct address_space {
struct inode *host; // 拥有者,可以是 inode 或 block_device。
struct xarray i_pages; // 缓存的页面。
struct rw_semaphore invalidate_lock; // 在无效操作期间,保护页缓存内容与文件偏移->磁盘块映射之间的一致性。它还用于阻止通过内存映射修改页缓存内容。
gfp_t gfp_mask; // 用于分配页面的内存分配标志。
atomic_t i_mmap_writable; // VM_SHARED 映射的数量。
#ifdef CONFIG_READ_ONLY_THP_FOR_FS
/* thp 的数量,仅用于非 shmem 文件 */
atomic_t nr_thps; // 页缓存中的 THP(非共享内存)数量。
#endif
struct rb_root_cached i_mmap; // 私有和共享映射的树。
unsigned long nrpages; // 页条目的数量,由 i_pages 锁保护。
pgoff_t writeback_index;// 写回从这里开始。
const struct address_space_operations *a_ops; // 方法。
unsigned long flags; // 错误位和标志(AS_*)。
struct rw_semaphore i_mmap_rwsem; // 保护 @i_mmap 和 @i_mmap_writable
errseq_t wb_err; // 最近发生的错误。
spinlock_t private_lock; // 供 address_space 的拥有者使用。
struct list_head private_list; // 供 address_space 的拥有者使用。
void *private_data; // 供 address_space 的拥有者使用。
} __attribute__((aligned(sizeof(long)))) __randomize_layout;
地址空间操作:
struct address_space_operations {
int (*writepage)(struct page *page, struct writeback_control *wbc); // 将文件在内存page中的更新到磁盘上
int (*read_folio)(struct file *, struct folio *); // 从磁盘上读取文件的数据到内存page中
/* 从此映射中回写一些脏页。 */
int (*writepages)(struct address_space *, struct writeback_control *); // 将多个page更新到磁盘上
/* 标记一个 folio 为脏页。如果此操作使其变脏,则返回 true */
bool (*dirty_folio)(struct address_space *, struct folio *);
void (*readahead)(struct readahead_control *);
int (*write_begin)(struct file *, struct address_space *mapping, // 要求具体文件系统准备将数据写到文件
loff_t pos, unsigned len,
struct page **pagep, void **fsdata);
int (*write_end)(struct file *, struct address_space *mapping, // 完成数据复制之后调用,具体文件系统 unlock page,释放引用计数,更新 i_size
loff_t pos, unsigned len, unsigned copied,
struct page *page, void *fsdata);
/* 不幸的是,FIBMAP 需要这个权宜之计。不要使用它 */
sector_t (*bmap)(struct address_space *, sector_t); // 将文件中的逻辑块扇区编号映射为对应设备上的物理块扇区编号
void (*invalidate_folio) (struct folio *, size_t offset, size_t len); // 使某个page部分或全部失效
bool (*release_folio)(struct folio *, gfp_t); // 日志文件系统使用,释放page
void (*free_folio)(struct folio *folio);
ssize_t (*direct_IO)(struct kiocb *, struct iov_iter *iter); // 绕过page cache
/*
* 将folio的内容移动到指定的目标,如果migrate_mode是MIGRATE_ASYNC,就不阻塞(异步)
*/
int (*migrate_folio)(struct address_space *, struct folio *dst,
struct folio *src, enum migrate_mode);
int (*launder_folio)(struct folio *); // 释放一个folio之前调用,回写dirty的folio
bool (*is_partially_uptodate) (struct folio *, size_t from, // 判断是否最新
size_t count);
void (*is_dirty_writeback) (struct folio *, bool *dirty, bool *wb);
int (*error_remove_page)(struct address_space *, struct page *); // 被内存故障处理代码使用
/* swapfile support */
int (*swap_activate)(struct swap_info_struct *sis, struct file *file,
sector_t *span);
void (*swap_deactivate)(struct file *file);
int (*swap_rw)(struct kiocb *iocb, struct iov_iter *iter);
};