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 ...
随机推荐
- 为什么使用 Executor 框架比使用应用创建和管理线程好?
为什么要使用 Executor 线程池框架 1.每次执行任务创建线程 new Thread()比较消耗性能,创建一个线程是比较耗 时.耗资源的. 2.调用 new Thread()创建的线程缺乏管理, ...
- TreeMap与TreeSet的源码分析
1.TreeMap源码 1.属性部分: private final Comparator<? super K> comparator;//比较器 private transient Ent ...
- 解释Spring支持的几种bean的作用域?
Spring框架支持以下五种bean的作用域: singleton :bean在每个Spring ioc 容器中只有一个实例. prototype:一个bean的定义可以有多个实例. request: ...
- 说说三元运算和if...else的相同之处
三元运算符和if-else语句:不同之处. a) 三元运算符是必须要有返回值,而if-else语句并不一定有返回值,其执行结果可能是赋值语句或者打印输出语句. b) java三元表达式有字符强转(双目 ...
- request表示HttpServletRequest对象?
request表示HttpServletRequest对象.它包含了有关浏览器请求的信息,并且提供了几个用于获取cookie, header, 和session数据的有用的方法. response表示 ...
- 处理器映射器(HandlerMapping)及处理器适配器(HandlerAdapter)详解(二)
注解的 处理器映射器 和 处理器适配器 介绍 注解的映射器: 在 Spring3.1 之前使用 DefaultAnnotationHandlerMapping 注解映射器(根据 DispatcherS ...
- 学习zabbix(六)
实验环境 实验用2到2台机器,实验所用机器系统环境如下,可以看到2台机器的主机名和IP地址 ? 1 2 3 4 5 6 7 8 9 10 [root@linux-node1 ~]# cat /etc/ ...
- CCF201512-2消除类游戏
问题描述 消除类游戏是深受大众欢迎的一种游戏,游戏在一个包含有n行m列的游戏棋盘上进行,棋盘的每一行每一列的方格上放着一个有颜色的棋子,当一行或一列上有连续三个或更多的相同颜色的棋子时,这些棋子都被消 ...
- C语言,最大公约数---更相减损术
// 最大公约数 更相减损法 int commonDivisor() { int i,k,n=0; printf("请输入两个不同的正整数,用,隔开\n"); scanf(&quo ...
- CRLF 漏洞学习和工具使用
原理 CRLF 指的是回车符(CR,ASCII 13,\r,%0d) 和换行符(LF,ASCII 10,\n,%0a),操作系统就是根据这个标识来进行换行的.但是如果对输入过滤不严,就会将恶意语句注入 ...