ucore lab8 文件系统 学习笔记
最后一战果然过瘾.代码量够多,新机制够复杂度,都管饱.做这一课就像从高山上往下走,坡急且路险,还不知自己的方位,琢磨不透系统的架构.待到下了山,回头一看豁然开朗,原来方才自己所下的山是这般模样.在这里面最重要的道具就是gdb的调用栈查看器了,没了它我肯定得迷失在深山里.
打过了难关就是舒坦,成就感满满,跟打游戏一样还想继续,下一次打哪个BOSS呢?一般就CSAPP吧.
概览
先放定义:
通用文件系统访问接口层(UFSAI):该层提供了一个从用户空间到文件系统的标准访问接口。这一层访问接口让应用程序能够通过一个简单的接口获得ucore内核的文件系统服务。
文件系统抽象层(VFS):向上提供一个一致的接口给内核其他部分(文件系统相关的系统调用实现模块和其他内核功能模块)访问。向下提供一个同样的抽象函数指针列表和数据结构屏蔽不同文件系统的实现细节。
Simple FS文件系统层(SFS):一个基于索引方式的简单文件系统实例。向上通过各种具体函数实现以对应文件系统抽象层提出的抽象函数。向下访问外设接口
外设接口层(DEV):向上提供device访问接口屏蔽不同硬件细节。向下实现访问各种具体设备驱动的接口,比如disk设备接口/串口设备接口/键盘设备接口等。
通用文件系统访问接口
主要向用户提供了开关读写的接口
[SYS_open] sys_open,
[SYS_close] sys_close,
[SYS_read] sys_read,
[SYS_write] sys_write,
[SYS_getdirentry] sys_getdirentry,
[SYS_seek] sys_seek,
[SYS_fstat] sys_fstat,
[SYS_fsync] sys_fsync,
[SYS_getcwd] sys_getcwd,
[SYS_dup] sys_dup,
VFS层
struct file: 进程访问的单个文件信息
struct file {
enum {
FD_NONE, FD_INIT, FD_OPENED, FD_CLOSED,
} status; //访问文件的执行状态
bool readable; //文件是否可读
bool writable; //文件是否可写
int fd; //文件在filemap中的索引值
off_t pos; //访问文件的当前位置
struct inode *node; //该文件对应的内存inode指针
int open_count; //打开此文件的次数
};
struct file_struct: 某个进程访问的当前工作目录和打开的文件集合
struct files_struct {
struct inode *pwd; //进程当前执行目录的内存inode指针
struct file *fd_array; //进程打开文件的数组
atomic_t files_count; //访问此文件的线程个数
semaphore_t files_sem; //确保对进程控制块中fs_struct的互斥访问
};
struct inode: 内存里的索引节点,封装了不同文件系统的索引节点
struct inode {
union {
//包含不同文件系统特定inode信息的union成员变量
struct device __device_info; //设备文件系统内存inode信息
struct sfs_inode __sfs_inode_info; //SFS文件系统内存inode信息
} in_info;
enum {
inode_type_device_info = 0x1234,
inode_type_sfs_inode_info,
} in_type; //此inode所属文件系统类型
atomic_t ref_count; //此inode的引用计数
atomic_t open_count; //打开此inode对应文件的个数
struct fs *in_fs; //抽象的文件系统,包含访问文件系统的函数指针
const struct inode_ops *in_ops; //抽象的inode操作,包含访问inode的函数指针
};
struct inode_ops: 封装了不同索引节点的操作函数列表(开关读写),vop为virtual operation简写
struct inode_ops {
unsigned long vop_magic;
int (*vop_open)(struct inode *node, uint32_t open_flags);
int (*vop_close)(struct inode *node);
int (*vop_read)(struct inode *node, struct iobuf *iob);
int (*vop_write)(struct inode *node, struct iobuf *iob);
int (*vop_getdirentry)(struct inode *node, struct iobuf *iob);
int (*vop_create)(struct inode *node, const char *name, bool excl, struct inode **
node_store);
int (*vop_lookup)(struct inode *node, char *path, struct inode **node_store);
......
};
SFS层
需要关注的文件类型有:
常规文件: 字节序列
目录: entry序列,包括文件名和关联的inode
设备文件: 物理设备映射成的文件
硬链接: 参考linux的硬链接
SFS的文件系统布局
每块4K
第0块 superblock: 识别魔数,总块数,未使用块数,SFS名
第1块 root-dir的inode
之后的部分: freemap及其他
索引节点:
inode,即index node.注意这里说的是SFS的索引节点,不是VFS抽象出的索引节点
struct sfs_disk_inode,代表了一个实际位于磁盘上的文件
struct sfs_disk_inode {
uint32_t size; //如果inode表示常规文件,则size是文件大小
uint16_t type; //inode的文件类型
uint16_t nlinks; //此inode的硬链接数
uint32_t blocks; //此inode的数据块数的个数
uint32_t direct[SFS_NDIRECT]; //此inode的直接数据块索引值(有SFS_NDIRECT
个)
uint32_t indirect; //此inode的一级间接数据块索引值
};
SFS_NDIRECT=12,即<=12 * 4k的数据块索引直接存在direct数组里,indirect=0;超过12 * 4k时direct=某一数据块索引值,该数据块全部用来存放剩余的数据块索引.因此UCORE最大支持\(12*4k+(4k/4)*4k=48k+4m\)的单个文件
对于普通文件,数据块中存放的就是文件的内容;
对于目录,数据块内包含了文件名和文件对应索引节点编号
/* file entry (on disk) */
struct sfs_disk_entry {
uint32_t ino; //索引节点所占数据块索引值
char name[SFS_MAX_FNAME_LEN + 1]; //文件名
};
为了方便,UCORE中直接用block(数据块)在磁盘上的编号作为inode编号
注意sfs_disk_inode是inode在磁盘上的存在形式,读入内存后为了方便判断否改写、互斥操作、回收和快速地定位等,我们为每个disk inode封装了一个sfs_inode
/* inode for sfs */
struct sfs_inode {
struct sfs_disk_inode *din; /* on-disk inode */
uint32_t ino; /* inode number */
uint32_t flags; /* inode flags */
bool dirty; /* true if inode modified */
int reclaim_count; /* kill inode if it hits zero */
semaphore_t sem; /* semaphore for din */
list_entry_t inode_link; /* entry for linked-list in sfs_fs*/
list_entry_t hash_link; /* entry for hash linked-list in sfs_fs
*/
};
同时还有一些辅助函数:
sfs_bmap_load_nolock: 将指定block载入内存,或创建一个新block并载入内存
sfs_bmap_truncate_nolock :释放掉指定inode的最后一个entry
sfs_dirent_read_nolock: 读取指定inode的第k个entry
sfs_dirent_search_nolock: 在指定目录下查找指定名字的inode的编号
设备文件IO层
关键数据结构
struct device: 表示一个设备及对应的开关读写操作,
struct device {
size_t d_blocks;
//设备占用的数据块个数
size_t d_blocksize;
//数据块的大小
int (*d_open)(struct device *dev, uint32_t open_flags);
//打开设备的函数指针
int (*d_close)(struct device *dev); //关闭设备的函数指针
int (*d_io)(struct device *dev, struct iobuf *iob, bool write); //读写设备的函数指针
int (*d_ioctl)(struct device *dev, int op, void *data); //用ioctl方式控制设备的函数指
针
};
在万物皆文件思想下,设备也是一种文件,所以需要关联的inode,strcut vfs_dev_t负责将device和inode关联
// device info entry in vdev_list
typedef struct {
const char *devname;
struct inode *devnode;
struct fs *fs;
bool mountable;
list_entry_t vdev_link;
} vfs_dev_t;
同时,所有设备通过vdev_list串连
文件系统初始化
vfs初始化:
vfs_init->vfs_devlist_init->list_init(&vdev_list)
dev初始化:
dev_init->dev_init_stdin
->dev_init_stdout
->dev_init_disk0
以stdin为例进行研究:
dev_init_stdin->dev_create_inode->inode_init 将node.ops与dev_node_ops绑定
->stdin_device_init 将device结构体的各个函数与stdin对应函数关联
->vfs_add_dev
//封装后的设备操作
static const struct inode_ops dev_node_ops = {
.vop_magic = VOP_MAGIC,
.vop_open = dev_open,
.vop_close = dev_close,
.vop_read = dev_read,
.vop_write = dev_write,
.vop_fstat = dev_fstat,
.vop_ioctl = dev_ioctl,
.vop_gettype = dev_gettype,
.vop_tryseek = dev_tryseek,
.vop_lookup = dev_lookup,
};
此时调用node的开关读写函数指向了dev的开关读写函数,进而调用node对应的device的开关读写函数.意味着整个dev层封装完成
//封装后的SFS文件操作
static const struct inode_ops sfs_node_fileops = {
.vop_magic = VOP_MAGIC,
.vop_open = sfs_openfile,
.vop_close = sfs_close,
.vop_read = sfs_read,
.vop_write = sfs_write,
.vop_fstat = sfs_fstat,
.vop_fsync = sfs_fsync,
.vop_reclaim = sfs_reclaim,
.vop_gettype = sfs_gettype,
.vop_tryseek = sfs_tryseek,
.vop_truncate = sfs_truncfile,
};
sfs初始化:
sfs_init->sfs_mount->vfs_mount->sfs_do_mount
在sfs_do_mount中完成对整个disk0文件系统的解析,并将disk0的数据读写操作封装成SFS的文件开关读写操作
读写操作分析
stdout的读写:
sys_write->sysfile_write->file_write->dev_write->stdout_io
stdin的读写:
sys_read->sysfile_read->file_read->dev_read_stdin_io
disk0_io的读写:
触发场景众多,这里只截取部分
sfs_init->sfs_mount->vfs_mount->sfs_do_mount->sfs_init_freemap->sfs_init_read->disk0_io
sfs_load_inode->sfs_rbuf->sfs_rwblock_nolock->disk0_io
sfs_dirent_read_nolock->sfs_rbuf->sfs_rbuf->sfs_rwblock_nolock->disk0_io
sfs_bmap_load_nolock->sfs_bmap_get_sub_nolock->sfs_rbuf->sfs_rwblock_nolock->disk0_io
可以发现stdin和stdout作为流设备是没有文件系统的,故没有SFS这一层
而disk0作为有文件系统的块设备,需要借助SFS才能操作
自下而上的总结
DEV层完成了对stdin,stdout,disk0的封装,使得可以分别通过对应的device结构体对他们进行操作
SFS层将disk0内的数据解析成了一套文件系统,在文件的开关读写和块设备读写间建立了桥梁
VFS层将各个stdio,stdout和SFS进行封装,提供了统一的读写接口VOP_XXX
UFSAI层将VFS接口再度封装成适合用户使用的形式
ucore lab8 文件系统 学习笔记的更多相关文章
- Hadoop学习笔记【分布式文件系统学习笔记】
分布式文件系统介绍 分布式文件系统:Hadoop Distributed File System,简称HDFS. 一.HDFS简介 Hadoop分布式文件系统(HDFS)被设计成适合运行在通用硬件(c ...
- ucore lab1 bootloader学习笔记
---恢复内容开始--- 开机流程回忆 以Intel 80386为例,计算机加电后,CPU从物理地址0xFFFFFFF0(由初始化的CS:EIP确定,此时CS和IP的值分别是0xF000和0xFFF0 ...
- Linux文件系统学习笔记-1
在Linux中, 一切皆文件,不论是目录,设备,套接字等都可以看成文件,而且每一个文件对应一个inode号,这是一一对应的关系. [root@oracle ~]# ls -il 总用量 2624 ...
- FAT文件系统规范v1.03学习笔记---4.文件和目录数据区之长目录项
1.前言 本文主要是对Microsoft Extensible Firmware Initiative FAT32 File System Specification中文翻译版的学习笔记. 每个FAT ...
- FAT文件系统规范v1.03学习笔记---3.根目录区之FAT目录项结构
1.前言 本文主要是对Microsoft Extensible Firmware Initiative FAT32 File System Specification中文翻译版的学习笔记. 每个FAT ...
- FAT文件系统规范v1.03学习笔记---1.保留区之 Fat32 FSInfo扇区结构和备份启动扇区
1.前言 本文主要是对Microsoft Extensible Firmware Initiative FAT32 File System Specification中文翻译版的学习笔记. 每个FAT ...
- FAT文件系统规范v1.03学习笔记---2.FAT区之FAT数据结构(Fat Data Structure)
1.前言 本文主要是对Microsoft Extensible Firmware Initiative FAT32 File System Specification中文翻译版的学习笔记. 每个FAT ...
- FAT文件系统规范v1.03学习笔记---1.保留区之启动扇区与BPB
1.前言 本文主要是对Microsoft Extensible Firmware Initiative FAT32 File System Specification中文翻译版的学习笔记. 每个FAT ...
- Linux学习笔记-文件系统和基本命令
目录 分区设备文件名 分区 挂载 文件目录 文件处理命令 目录处理命令 硬件设备文件名 IDE硬盘 /dev/hd[a-d] USB硬盘 /dev/sd[a-p] 光驱 /dev/cdrom或者/de ...
随机推荐
- spring-boot 注解解析
package com.hllq.quan.controller; import com.hllq.quan.mapper.WeiboUserMapper; import com.hllq.quan. ...
- “a==b”和”a.equals(b)”有什么区别?
如果 a 和 b 都是对象,则 a==b 是比较两个对象的引用,只有当 a 和 b 指 向的是堆中的同一个对象才会返回 true,而 a.equals(b) 是进行逻辑比较,所以 通常需要重写该方法来 ...
- 220v-5v稳压电路
5V整流电路原理 先对电路进行整流 整流电路:利用单向导电器件将交流电转换成脉动直流电路,再用电容进行滤波 滤波电路:利用储能元件(电感或电容)把脉动直流电转换成比较平坦的直流电,然后对电路进行稳压 ...
- Asp.Net Core之Identity应用(下篇)
一.前言 在上篇中简单介绍了 Asp.Net Core 自带的 Identity,一个负责对用户的身份进行认证的框架,当我们按需选择这个框架作为管理和存储我们应用中的用户账号数据的时候,就会添加到自己 ...
- logback日志级别动态切换的终极方案(asm使用)
背景 一切皆有因果,所有事情,都有事件驱动.本方案的日志级别切换是由这样的背景下产生的: 单个生产环境上,有几百近千个微服务 日志级别切换不重启服务,要求即时生效果 由业务开发人员去修改代码或增加相关 ...
- elf,基于flexbox的响应式CSS框架
官网地址:http://jrainlau.github.io/elf/项目地址:https://github.com/jrainlau/elf 介绍 取名为"精灵"的elf,是一个 ...
- 腾讯云+社区开发者大会开启报名,WeGeek 邀你一起聊聊小程序
刚满 2 岁的微信小程序,正给我们带来一种全新轻便的生活方式. 内测时的青涩还历历在目,到现在,小程序生态已日渐成熟.超过 150 万开发者在这里找到了自己的新天地,打磨出超过 100 万个小程序. ...
- 五款轻量型bug管理工具横向测评
五款轻量型bug管理工具横向测评 最近正在使用的本地bug管理软件又出问题了,已经记不清这是第几次了,每次出现问题都要耗费大量的时间精力去网上寻找解决方案,劳心劳力.为了避免再次出现这样的情况,我决定 ...
- js中的bool值转换及"&&" 、"||"、 "!!"详解
bool值转换 数据类型 bool值转化 undefined undefined 转化为 false Object null 转化为false,其他为 true Boolean false 转化为 f ...
- 前端面试题整理——HTML/CSS
如何理解语义化: 对应的内容是用相应意思的标签,增加开发者和机器爬虫对代码的可读性. 块状元素和内联元素: 块状元素有:display:block/table:有div h1 h2 table ul ...