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 ...
随机推荐
- String s = new String("xyz");创建了几个字符串对象?
两个对象,一个是静态区的"xyz",一个是用new创建在堆上的对象.
- rabbitmq 中 vhost 的作用是什么?
vhost本质上是一个mini版的RabbitMQ服务器,拥有自己的队列.绑定.交换器和权限控制: vhost通过在各个实例间提供逻辑上分离,允许你为不同应用程序安全保密地运行数据: vhost是AM ...
- redis有哪些功能
基于本机内存的缓存 为了解决调用API依然需要2秒的问题,经过排查,其主要原因在于使用SQL获取热点新闻的过程中消耗了将近2秒的时间,于是乎,我们又想到了一个简单粗暴的解决方案,即把SQL查询的结果直 ...
- 详细描述一下 Elasticsearch 索引文档的过程?
协调节点默认使用文档 ID 参与计算(也支持通过 routing),以便为路由提供合适的分片. shard = hash(document_id) % (num_of_primary_shards) ...
- lvs dr 模式请求过程
一. lvs dr 模式请求过程 1.整个请求过程如下: client在发起请求之前,会发一个arp广播的包,在网络中找"谁是vip",由于所有的服务器,lvs和rs都有vip,为 ...
- WzwJDBC 自定义工具类(获取连接,释放资源)
package wzwUtil;import java.io.IOException;import java.io.InputStream;import java.sql.*;import java. ...
- DOM 小总结
DOM 是什么 文档对象模型,是针对 HTML 和 XML 文档的一个 API (应用程序编程接口), 描绘了一个层次化的节点树. D: document 当 web 浏览器浏览一个页面的时候,DOM ...
- electron制作聊天界面(仿制qq)
效果图: 样式使用scss和flex布局 这也是制作IM系统的最后一个界面了!在制作之前参考了qq和千牛 需要注意的点 qq将滚动条美化了 而且在无操作的情况下是不会显示的 滚动条美化 ::-webk ...
- python爬虫---爬取王者荣耀全部皮肤图片
代码: import requests json_headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win ...
- DB2表数据导出、导入及常用sql使用总结
一.DB2数据的导出: export to [path(例:D:"TABLE1.ixf)]of ixf select [字段(例: * or col1,col2,col3)] from ...