从零到一手写操作系统(十、文件系统 2)linux虚拟文件系统)
如果追忆会荡起涟漪,那么今天的秋红落叶和晴空万里都归你
https://aeneag.xyz
微信公众号:技术乱舞
艾恩凝
手写操作系统目录
10.2)linux虚拟文件系统
什么是VFS?
可以理解为通用文件系统抽象层。在 Linux 中,支持 EXT、XFS、JFS、BTRFS、FAT、NTFS 等多达十几种不同的文件系统,但不管在什么储存设备上使用什么文件系统,也不管访问什么文件,都可以统一地使用一套 open(), read()、write()、close() 这样的接口。
数据结构
超级块
1struct super_block {
2 struct list_head s_list; //超级块链表
3 dev_t s_dev; //设备标识
4 unsigned char s_blocksize_bits;//以位为单位的块大小
5 unsigned long s_blocksize;//以字节为单位的块大小
6 loff_t s_maxbytes; //一个文件最大多少字节
7 struct file_system_type *s_type; //文件系统类型
8 const struct super_operations *s_op;//超级块函数集合
9 const struct dquot_operations *dq_op;//磁盘限额函数集合
10 unsigned long s_flags;//挂载标志
11 unsigned long s_magic;//文件系统魔数
12 struct dentry *s_root;//挂载目录
13 struct rw_semaphore s_umount;//卸载信号量
14 int s_count;//引用计数
15 atomic_t s_active;//活动计数
16 struct block_device *s_bdev;//块设备
17 void *s_fs_info;//文件系统信息
18 time64_t s_time_min;//最小时间限制
19 time64_t s_time_max;//最大时间限制
20 char s_id[32]; //标识名称
21 uuid_t s_uuid; //文件系统的UUID
22 struct list_lru s_dentry_lru;//LRU方式挂载的目录
23 struct list_lru s_inode_lru;//LRU方式挂载的索引结点
24 struct mutex s_sync_lock;//同步锁
25 struct list_head s_inodes; //所有的索引节点
26 spinlock_t s_inode_wblist_lock;//回写索引节点的锁
27 struct list_head s_inodes_wb; //挂载所有要回写的索引节点
28} __randomize_layout;
超级块函数
1struct super_operations {
2 //分配一个新的索引结点结构
3 struct inode *(*alloc_inode)(struct super_block *sb);
4 //销毁给定的索引节点
5 void (*destroy_inode)(struct inode *);
6 //释放给定的索引节点
7 void (*free_inode)(struct inode *);
8 //VFS在索引节点为脏(改变)时,会调用此函数
9 void (*dirty_inode) (struct inode *, int flags);
10 //该函数用于将给定的索引节点写入磁盘
11 int (*write_inode) (struct inode *, struct writeback_control *wbc);
12 //在最后一个指向索引节点的引用被释放后,VFS会调用该函数
13 int (*drop_inode) (struct inode *);
14 void (*evict_inode) (struct inode *);
15 //减少超级块计数调用
16 void (*put_super) (struct super_block *);
17 //同步文件系统调用
18 int (*sync_fs)(struct super_block *sb, int wait);
19 //释放超级块调用
20 int (*freeze_super) (struct super_block *);
21 //释放文件系统调用
22 int (*freeze_fs) (struct super_block *);
23 int (*thaw_super) (struct super_block *);
24 int (*unfreeze_fs) (struct super_block *);
25 //VFS通过调用该函数,获取文件系统状态
26 int (*statfs) (struct dentry *, struct kstatfs *);
27 //当指定新的安装选项重新安装文件系统时,VFS会调用此函数
28 int (*remount_fs) (struct super_block *, int *, char *);
29 //VFS调用该函数中断安装操作。该函数被网络文件系统使用,如NFS
30 void (*umount_begin) (struct super_block *);
31};
有了超级块和超级块函数集合结构,VFS 就能让一个文件系统的信息和表示变得规范了。也就是说,文件系统只要实现了 super_block 和 super_operations 两个结构,就可以插入到 VFS 中了。
目录结构
图中显示了 Linux 文件目录情况,也显示了一个设备上的文件系统是如何挂载到某个目录下的。
1//快速字符串保存关于字符串的 "元数据"(即长度和哈希值)
2struct qstr {
3 union {
4 struct {
5 HASH_LEN_DECLARE;
6 };
7 u64 hash_len;
8 };
9 const unsigned char *name;//指向名称字符串
10};
11struct dentry {
12 unsigned int d_flags; //目录标志
13 seqcount_spinlock_t d_seq; //锁
14 struct hlist_bl_node d_hash;//目录的哈希链表
15 struct dentry *d_parent; //指向父目录
16 struct qstr d_name; //目录名称
17 struct inode *d_inode; //指向目录文件的索引节点
18 unsigned char d_iname[DNAME_INLINE_LEN]; //短目录名
19 struct lockref d_lockref; //目录锁与计数
20 const struct dentry_operations *d_op;//目录的函数集
21 struct super_block *d_sb; //指向超级块
22 unsigned long d_time; //时间
23 void *d_fsdata; //指向具体文件系统的数据
24 union {
25 struct list_head d_lru; //LRU链表
26 wait_queue_head_t *d_wait;
27 };
28 struct list_head d_child; //挂入父目录的链表节点
29 struct list_head d_subdirs; //挂载所有子目录的链表
30} __randomize_layout;
dentry 结构中包含了目录的名字和挂载子目录的链表,同时也能指向父目录。但是需要注意的是,目录也是文件,需要用 inode 索引结构来管理目录文件数据。
目录文件函数
1struct dentry_operations {
2 //该函数判断目录对象是否有效
3 int (*d_revalidate)(struct dentry *, unsigned int);
4 int (*d_weak_revalidate)(struct dentry *, unsigned int);
5 //该函数为目录项生成散列值,当目录项要加入散列表中时,VFS调用该函数
6 int (*d_hash)(const struct dentry *, struct qstr *);
7 //VFS调用该函数来比较name1和name2两个文件名。多数文件系统使用VFS的默认操作,仅做字符串比较。对于有些文件系统,比如FAT,简单的字符串比较不能满足其需要,因为 FAT文件系统不区分大小写
8 int (*d_compare)(const struct dentry *,
9 unsigned int, const char *, const struct qstr *);
10 //当目录项对象的计数值等于0时,VFS调用该函数
11 int (*d_delete)(const struct dentry *);
12 //当分配目录时调用
13 int (*d_init)(struct dentry *);
14 //当目录项对象要被释放时,VFS调用该函数,默认情况下,它什么也不做
15 void (*d_release)(struct dentry *);
16 void (*d_prune)(struct dentry *);
17 //当一个目录项对象丢失了相关索引节点时,VFS调用该函数。默认情况下VFS会调用iput()函数释放索引节点
18 void (*d_iput)(struct dentry *, struct inode *);
19 //当需要生成一个dentry的路径名时被调用
20 char *(*d_dname)(struct dentry *, char *, int);
21 //当要遍历一个自动挂载时被调用(可选),这应该创建一个新的VFS挂载记录并将该记录返回给调用者
22 struct vfsmount *(*d_automount)(struct path *);
23 //文件系统管理从dentry的过渡(可选)时,被调用
24 int (*d_manage)(const struct path *, bool);
25 //叠加/联合类型的文件系统实现此方法
26 struct dentry *(*d_real)(struct dentry *, const struct inode *);
27} ____cacheline_aligned;
文件索引结点
VFS 用 inode 结构表示一个文件索引结点,它里面包含文件权限、文件所属用户、文件访问和修改时间、文件数据块号等一个文件的全部信息,一个 inode 结构就对应一个文件。
1struct inode {
2 umode_t i_mode;//文件访问权限
3 unsigned short i_opflags;//打开文件时的标志
4 kuid_t i_uid;//文件所属的用户id
5 kgid_t i_gid;//文件所属的用户组id
6 unsigned int i_flags;//标志
7 const struct inode_operations *i_op;//inode函数集
8 struct super_block *i_sb;//指向所属超级块
9 struct address_space *i_mapping;//文件数据在内存中的页缓存
10 unsigned long i_ino;//inode号
11 dev_t i_rdev;//实际设备标志符
12 loff_t i_size;//文件大小,以字节为单位
13 struct timespec64 i_atime;//文件访问时间
14 struct timespec64 i_mtime;//文件修改时间
15 struct timespec64 i_ctime;//最后修改时间
16 spinlock_t i_lock; //保护inode的自旋锁
17 unsigned short i_bytes;//使用的字节数
18 u8 i_blkbits;//以位为单位的块大小;
19 u8 i_write_hint;
20 blkcnt_t i_blocks;
21 struct list_head i_io_list;
22 struct list_head i_lru; //在缓存LRU中的链表节点
23 struct list_head i_sb_list;//在超级块中的链表节点
24 struct list_head i_wb_list;
25 atomic64_t i_version;//版本号
26 atomic64_t i_sequence;
27 atomic_t i_count;//计数
28 atomic_t i_dio_count;//直接io进程计数
29 atomic_t i_writecount;//写进程计数
30 union {
31 const struct file_operations *i_fop;//文件函数集合
32 void (*free_inode)(struct inode *);
33 };
34 struct file_lock_context *i_flctx;
35 struct address_space i_data;
36 void *i_private; //私有数据指针
37} __randomize_layout;
文件函数
1struct inode_operations {
2 //VFS通过系统create()和open()接口来调用该函数,从而为dentry对象创建一个新的索引节点
3 int (*create) (struct inode *, struct dentry *,int);
4 //该函数在特定目录中寻找索引节点,该索引节点要对应于dentry中给出的文件名
5 struct dentry * (*lookup) (struct inode *, struct dentry *);
6 //被系统link()接口调用,用来创建硬连接。硬链接名称由dentry参数指定
7 int (*link) (struct dentry *, struct inode *, struct dentry *);
8 //被系统unlink()接口调用,删除由目录项dentry链接的索引节点对象
9 int (*unlink) (struct inode *, struct dentry *);
10 //被系统symlik()接口调用,创建符号连接,该符号连接名称由symname指定,连接对象是dir目录中的dentry目录项
11 int (*symlink) (struct inode *, struct dentry *, const char *);
12 //被mkdir()接口调用,创建一个新目录。
13 int (*mkdir) (struct inode *, struct dentry *, int);
14 //被rmdir()接口调用,删除dentry目录项代表的文件
15 int (*rmdir) (struct inode *, struct dentry *);
16 //被mknod()接口调用,创建特殊文件(设备文件、命名管道或套接字)。
17 int (*mknod) (struct inode *, struct dentry *, int, dev_t);
18 //VFS调用该函数来移动文件。文件源路径在old_dir目录中,源文件由old_dentry目录项所指定,目标路径在new_dir目录中,目标文件由new_dentry指定
19 int (*rename) (struct inode *, struct dentry *, struct inode *, struct dentry *);
20 //被系统readlink()接口调用,拷贝数据到特定的缓冲buffer中。拷贝的数据来自dentry指定的符号链接
21 int (*readlink) (struct dentry *, char *, int);
22 //被VFS调用,从一个符号连接查找他指向的索引节点
23 int (*follow_link) (struct dentry *, struct nameidata *);
24 //在follow_link()调用之后,该函数由vfs调用进行清除工作
25 int (*put_link) (struct dentry *, struct nameidata *);
26 //被VFS调用,修改文件的大小,在调用之前,索引节点的i_size项必须被设置成预期的大小
27 void (*truncate) (struct inode *);
28 //该函数用来检查给定的inode所代表的文件是否允许特定的访问模式,如果允许特定的访问模式,返回0,否则返回负值的错误码
29 int (*permission) (struct inode *, int);
30 //被notify_change接口调用,在修改索引节点之后,通知发生了改变事件
31 int (*setattr) (struct dentry *, struct iattr *);
32 //在通知索引节点需要从磁盘中更新时,VFS会调用该函数
33 int (*getattr) (struct vfsmount *, struct dentry *, struct kstat *);
34 //被VFS调用,向dentry指定的文件设置扩展属性
35 int (*setxattr) (struct dentry *, const char *, const void *, size_t, int);
36 //被VFS调用,拷贝给定文件的扩展属性name对应的数值
37 ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);
38 //该函数将特定文件所有属性列表拷贝到一个缓冲列表中
39 ssize_t (*listxattr) (struct dentry *, char *, size_t);
40 //该函数从给定文件中删除指定的属性
41 int (*removexattr) (struct dentry *, const char *);
42};
文件
1struct file {
2 union {
3 struct llist_node fu_llist;
4 struct rcu_head fu_rcuhead;
5 } f_u;
6 struct path f_path; //文件路径
7 struct inode *f_inode; //文件对应的inode
8 const struct file_operations *f_op;//文件函数集合
9 spinlock_t f_lock; //自旋锁
10 enum rw_hint f_write_hint;
11 atomic_long_t f_count;//文件对象计数据。
12 unsigned int f_flags;//文件标志
13 fmode_t f_mode;//文件权限
14 struct mutex f_pos_lock;//文件读写位置锁
15 loff_t f_pos;//进程读写文件的当前位置
16 u64 f_version;//文件版本
17 void *private_data;//私有数据
18} __randomize_layout
关系
文件操作
打开
读写
关闭
主要了解了linux中的文件系统,需要学习的东西还有很多
注:本节于2022年5月13日结束
手写操作系统目录
吾心信其可行,
则移山填海之难,
终有成功之日!
——孙文
则移山填海之难,
终有成功之日!
——孙文
评论
0 评论