- 作者: 陈孝松
- 主页: chenxiaosong.com
- 哔哩哔哩教学视频: 陈孝松
- 课程: chenxiaosong.com/courses
- 博客: chenxiaosong.com/blog
- 贡献: chenxiaosong.com/contributions
- 邮箱: chenxiaosong@chenxiaosong.com
- QQ交流群: 544216206, 点击查看群介绍
一般的Linux书籍都是先讲解进程和内存相关的知识,但我想先讲解文件系统。
第一,因为我就是做文件系统的,更擅长这一块,其他模块的内容我还要再去好好看看书,毕竟不能误人子弟嘛;第二,是
因为文件系统模块更接近于用户态,是相对比较好理解的内容(当然想深入还是要下大功夫的),由文件系统入手比较适合初学者。
虚拟文件系统英文全称Virtual file system,缩写为VFS,又称为虚拟文件切换系统(virtual filesystem switch)。所有的文件系统都要先经过虚拟文件系统层,虚拟文件系统相当于制定了一套规则,如果你想写一个新的文件系统,只需要遵守这套规则就可以了。
VFS虽然是用C语言写的,但使用了面向对象的设计思路。
file_system_type
描述各种特定文件系统类型,每种文件系统只有一个file_system_type
对象,具体文件系统如ext2模块加载时调用init_ext2_fs() -> register_filesystem()
注册。根文件系统类型rootfs_fs_type
。
struct file_system_type {
const char *name; // 名字
int fs_flags; // 类型标志
#define FS_REQUIRES_DEV 1
#define FS_BINARY_MOUNTDATA 2
#define FS_HAS_SUBTYPE 4
#define FS_USERNS_MOUNT 8 /* 可以由用户命名空间根目录挂载 */
#define FS_DISALLOW_NOTIFY_PERM 16 /* 禁用 fanotify 权限事件 */
#define FS_ALLOW_IDMAP 32 /* 文件系统已更新以处理 vfs id 映射。 */
#define FS_RENAME_DOES_D_MOVE 32768 /* 文件系统将在内部处理 rename() 时的 d_move()。 */
int (*init_fs_context)(struct fs_context *);
const struct fs_parameter_spec *parameters;
struct dentry *(*mount) (struct file_system_type *, int, // 从磁盘中读取超级块
const char *, void *);
void (*kill_sb) (struct super_block *); // 终止访问超级块
struct module *owner; // 文件系统模块
struct file_system_type * next; // 链表中下一个文件系统类型
struct hlist_head fs_supers; // 超级块对象链表
// 运行时使锁生效
struct lock_class_key s_lock_key;
struct lock_class_key s_umount_key;
struct lock_class_key s_vfs_rename_key;
struct lock_class_key s_writers_key[SB_FREEZE_LEVELS];
struct lock_class_key i_lock_key;
struct lock_class_key i_mutex_key;
struct lock_class_key invalidate_lock_key;
struct lock_class_key i_mutex_dir_key;
};
文件系统挂载时,有一个mount
结构体在挂载点被创建,代表文件系统实例,也就是代表一个挂载点。
struct mount {
struct hlist_node mnt_hash; // 散列表
struct mount *mnt_parent; // 父文件系统
struct dentry *mnt_mountpoint; // 挂载点的目录项
struct vfsmount mnt;
union {
struct rcu_head mnt_rcu;
struct llist_node mnt_llist;
};
#ifdef CONFIG_SMP
struct mnt_pcp __percpu *mnt_pcp;
#else
int mnt_count; // 引用计数
int mnt_writers; // 写者引用计数
#endif
struct list_head mnt_mounts; /* 子文件系统链表, 固定在此 */
struct list_head mnt_child; /* 子文件系统链表 */
struct list_head mnt_instance; /* sb->s_mounts 上的挂载实例 */
const char *mnt_devname; /* 设备名称,例如 /dev/dsk/hda1 */
struct list_head mnt_list; // 描述符链表
struct list_head mnt_expire; /* 在到期链表的位置 */
struct list_head mnt_share; /* 在共享安装链表的位置 */
struct list_head mnt_slave_list;/* 从安装链表 */
struct list_head mnt_slave; /* 在从安装链表的位置 */
struct mount *mnt_master; /* 从安装链表的主人 */
struct mnt_namespace *mnt_ns; /* 相关的命名空间 */
struct mountpoint *mnt_mp; /* 挂载的位置 */
union {
struct hlist_node mnt_mp_list; /* 具有相同挂载点的挂载链表 */
struct hlist_node mnt_umount;
};
struct list_head mnt_umounting; /* 用于卸载传播的列表条目 */
#ifdef CONFIG_FSNOTIFY
struct fsnotify_mark_connector __rcu *mnt_fsnotify_marks;
__u32 mnt_fsnotify_mask;
#endif
int mnt_id; /* 安装标识符 */
int mnt_group_id; /* 组标识符 */
int mnt_expiry_mark; /* 到期时为1 */
struct hlist_head mnt_pins;
struct hlist_head mnt_stuck_children;
} __randomize_layout;
struct vfsmount {
struct dentry *mnt_root; /* 该文件系统的根目录项 */
struct super_block *mnt_sb; /* 超级块 */
int mnt_flags; // 挂载标志, MNT_NOSUID 等
struct mnt_idmap *mnt_idmap;
} __randomize_layout;
files_struct
描述单个进程相关的信息,struct task_struct
中的files
成员指向它。
/*
* /* 打开的文件表结构 */
*/
struct files_struct {
/*
* 主要用于读取的部分
*/
atomic_t count; // 引用计数
bool resize_in_progress;
wait_queue_head_t resize_wait;
struct fdtable __rcu *fdt; // 如果打开的文件数大于NR_OPEN_DEFAULT,分配一个新数组
struct fdtable fdtab; // 基fd表
/*
* 在 SMP 中,写入部分位于单独的缓存行
*/
spinlock_t file_lock ____cacheline_aligned_in_smp; // 单个文件的锁
unsigned int next_fd; // 缓存下一个可用的fd
unsigned long close_on_exec_init[1]; // exec()时关闭的fd链表
unsigned long open_fds_init[1]; // 打开的fd链表
unsigned long full_fds_bits_init[1];
struct file __rcu * fd_array[NR_OPEN_DEFAULT]; // 默认的文件对象数组
};
fs_struct
表示文件系统进程相关的信息,struct task_struct
中的fs
成员指向它。
struct fs_struct {
int users; // 用户数目
spinlock_t lock; // 保护该结构体的锁
seqcount_spinlock_t seq;
int umask; // 掩码
int in_exec; // 当前正在执行的文件
struct path root; // 根目录路径
struct path pwd; // 当前工作目录的路径
} __randomize_layout;
mnt_namespace
表示单进程命名空间,struct task_struct
中的nsproxy->mnt_namespace
成员指向它。
struct mnt_namespace {
struct ns_common ns;
struct mount * root; // 根目录的挂载点
/*
* 对 .list 的遍历和修改受以下任意一种方式保护:
* - 获取 namespace_sem 的写锁,或
* - 获取 namespace_sem 的读锁并获取 .ns_lock
*/
struct list_head list; // 挂载点链表
spinlock_t ns_lock;
struct user_namespace *user_ns;
struct ucounts *ucounts; // 用户计数
u64 seq; /* 防止循环的序列号 */
wait_queue_head_t poll; // 轮询的等待队列
u64 event; // 事件计数
unsigned int mounts; /* 命名空间中的挂载数量 */
unsigned int pending_mounts;
} __randomize_layout;
struct ucounts {
struct hlist_node node;
struct user_namespace *ns;
kuid_t uid;
atomic_t count; // 引用计数
atomic_long_t ucount[UCOUNT_COUNTS];
atomic_long_t rlimit[UCOUNT_RLIMIT_COUNTS];
};
还有文件锁的数据结构为struct file_lock
。