- 作者: 陈孝松
- 主页: 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语言写的,但使用了面向对象的设计思路。
超级块英文全称是super block,存储特定文件系统的信息。如果是基于磁盘的文件系统,通常对应磁盘上特定扇区中的数据。如果不是基于磁盘的文件系统(如procfs或sysfs),会在使用时创建超级块,只保留在内存中。
超级块对象结构体定义在文件include/linux/fs.h
中,比较长,不用背,用到时查一下就好,我会在这里加一些中文注释。
struct super_block {
struct list_head s_list; /* 放在最开头,指向 super_blocks,使用list_add_tail加到super_blocks链表中 */
dev_t s_dev; /* 设备标识符 */
unsigned char s_blocksize_bits; // 块大小,单位: bit
unsigned long s_blocksize; // 块大小,单位: 字节
loff_t s_maxbytes; /* 文件大小上限 */
struct file_system_type *s_type; // 文件系统类型
const struct super_operations *s_op; // 超级块方法
const struct dquot_operations *dq_op; // 磁盘限额方法
const struct quotactl_ops *s_qcop; // 限额控制方法
const struct export_operations *s_export_op; // 导出方法
unsigned long s_flags; // 挂载标志
unsigned long s_iflags; /* 内部 SB_I_* 标志 */
unsigned long s_magic; // 文件系统幻数
struct dentry *s_root; // 目录挂载点
struct rw_semaphore s_umount; // 卸载信号量
int s_count; // 超级块引用计数
atomic_t s_active; // 活动引用计数
#ifdef CONFIG_SECURITY
void *s_security; // 安全模块
#endif
const struct xattr_handler **s_xattr; // 扩展的属性操作
#ifdef CONFIG_FS_ENCRYPTION
const struct fscrypt_operations *s_cop;
struct fscrypt_keyring *s_master_keys; /* 主加密密钥正在使用 */
#endif
#ifdef CONFIG_FS_VERITY
const struct fsverity_operations *s_vop;
#endif
#if IS_ENABLED(CONFIG_UNICODE)
struct unicode_map *s_encoding;
__u16 s_encoding_flags;
#endif
struct hlist_bl_head s_roots; /* NFS 的备用根目录项 */
struct list_head s_mounts; /* 挂载点列表;_不_用于文件系统,struct mount的mnt_instance加到这个链表中 */
struct block_device *s_bdev; // 相关的块设备
struct backing_dev_info *s_bdi;
struct mtd_info *s_mtd; // 存储磁盘信息
struct hlist_node s_instances; // 这种类型的所有文件系统
unsigned int s_quota_types; /* 支持的配额类型的位掩码 */
struct quota_info s_dquot; /* 限额相关选项 */
struct sb_writers s_writers;
/*
* 将 s_fs_info, s_time_gran, s_fsnotify_mask 和
* s_fsnotify_marks 放在一起以提高缓存效率。
* 它们经常被访问但很少被修改。
*/
void *s_fs_info; /* 文件系统私有信息 */
/* c/m/atime 的精度(以纳秒为单位,不能超过一秒) */
u32 s_time_gran; // 时间戳粒度
/* c/m/atime 的时间限制(以秒为单位) */
time64_t s_time_min;
time64_t s_time_max;
#ifdef CONFIG_FSNOTIFY
__u32 s_fsnotify_mask;
struct fsnotify_mark_connector __rcu *s_fsnotify_marks;
#endif
char s_id[32]; /* 信息性名称,文本名字 */
uuid_t s_uuid; /* Universally Unique Identifier"(全局唯一标识符) */
unsigned int s_max_links;
/*
* 下一个字段仅供 VFS 使用。任何文件系统都没有权利查看它。
* 你已经被警告过了。
*/
struct mutex s_vfs_rename_mutex; /* Kludge,重命名锁 */
/*
* 文件系统子类型。如果非空,/proc/mounts 中的文件系统类型字段
* 将是 "type.subtype"
*/
const char *s_subtype; // 子类型名称
const struct dentry_operations *s_d_op; /* 目录项的默认 d_op */
struct shrinker s_shrink; /* 每个超级块的收缩器句柄 */
/* nlink == 0 但仍被引用的 inode 数量 */
atomic_long_t s_remove_count;
/*
* 被监视的 inode/mount/sb 对象的数量,注意 inode 对象目前被双重计算。
*/
atomic_long_t s_fsnotify_connectors;
/* 超级块的只读状态正在被更改 */
int s_readonly_remount;
/* 每个超级块的 errseq_t 用于通过 syncfs 报告回写错误 */
errseq_t s_wb_err;
/* 从中断上下文推迟的 AIO 完成 */
struct workqueue_struct *s_dio_done_wq;
struct hlist_head s_pins;
/*
* 拥有的用户命名空间和默认上下文,用于解释文件系统的 uid、gid、配额、
* 设备节点、xattrs 和安全标签。
*/
struct user_namespace *s_user_ns;
/*
* list_lru 结构本质上只是指向每个节点 lru 列表表格的指针,
* 每个节点都有自己的自旋锁。没有必要将它们放入不同的缓存行。
*/
struct list_lru s_dentry_lru; // 未被使用目录项链表
struct list_lru s_inode_lru;
struct rcu_head rcu;
struct work_struct destroy_work;
struct mutex s_sync_lock; /* 同步序列化锁 */
/*
* 指示该超级块在文件系统栈中的深度
*/
int s_stack_depth;
/* s_inode_list_lock 保护 s_inodes */
spinlock_t s_inode_list_lock ____cacheline_aligned_in_smp;
struct list_head s_inodes; /* 索引节点链表 */
spinlock_t s_inode_wblist_lock;
struct list_head s_inodes_wb; /* 回写的 inode */
} __randomize_layout;
超级块对象通过alloc_super()
函数创建和初始化,具体的文件系统如ext2文件系统的流程如下:
mount // 系统调用
do_mount
path_mount
do_new_mount
vfs_get_tree
legacy_get_tree
ext2_mount // ext2_fs_type的.mount方法
mount_bdev
sget
alloc_super