Lab5
关键词:文件系统,IDE磁盘,磁盘驱动
实验内容
概览
- 外部存储设备驱动:实现 IDE 磁盘的用户态驱动程序,该驱动程序将通过系统调用的方式陷入内核,对磁盘镜像进行读写。
- 文件系统结构:实现模拟磁盘的驱动程序以及磁盘上和操作系统中的文件系统结构,并实现文件系统操作的相关函数。
- 文件系统的用户接口:为用户提供接口和机制使得用户程序能够使用文件系统,这主要通过一个用户态的文件系统服务来实现。
文件系统
尽管进程运行时可以在它的地址存储空间存储一定量的信息,但存在:(1) 存储容量受虚拟地址空间大小限制,(2) 进程终止后信息丢失,(3) 不能让多个进程并发访问相关信息,等缺陷。而文件是对磁盘的建模,可以实现长期存储信息。
设备,MMIO与操作系统对设备的控制
-
CPU(程序) 通过读写设备上的寄存器来和设备进行通信
-
MIPS 体系结构使用 **MMIO(内存映射 IO)**机制访问设备寄存器
- MMIO 使用不同的物理内存地址给设备寄存器编址。这种编址方式将一部分对物理内存的访问"重定向"到设备地址空间中, CPU尝试访问这部分物理内存的时候, 实际上最终是访问了相应的设备
-
MIPS 体系结构中程序需要通过 KSEG1 段访问不可被缓存的外设
-
优点:可以通过和访问内存相同的方式进行对外设的访问,简化编程接口;可以直接访问外设寄存器,无需中间层来进行访问。
以下是 GXemul的仿真设备的说明:
consoles | 0x10000000 | A simple console device, for writing characters to the controlling terminal and receiving keypresses. |
disk | 0x13000000 | Disk controller, which can read from and write to emulated IDE disks. It does not use interrupts; read and write operations finish instantaneously. |
rtc | 0x15000000 | A Real-Time Clock, used to retrieve the current time and to cause periodic interrupts. |
IDE 磁盘驱动
IDE磁盘驱动位于用户空间,用户态进程直接读写 kseg1 会引发错误,需要通过以下两个系统调用实现用户态读写设备。
系统调用实现设备读写
// va: 用户虚拟地址; pa: 设备的物理地址; len: 读写的长度 |
磁盘用户态驱动程序
扇区是磁盘读写的基本单位,在本实验中扇区大小为512字节。GXemul 提供的 Simulated IDE disk 的地址是 0x13000000。
当需要从磁盘的指定位置读取一个扇区时,驱动程序会调用 ide_read 函数来讲磁盘中对应的 sector 的数据读到设备缓冲区中,读取流程如下:
遍历所有要读取的扇区,进行以下操作:
- 设置 IDE disk 的 ID,即将 diskno 写入 0xB3000010;
- 将相对于磁盘起始位置的偏移量写入 0xB3000000,注意此处偏移量为 。
- 向内存 0xB3000020写入0表示磁盘开始读操作(若写入1,表示磁盘开始写操作);
- 从 0xB3000030获取操作的状态返回值,若成功(非0),就将这个sector的数据从设备缓冲区(0xB3004000~0xB30041ff)中拷贝到目标位置(dst + off)。(写操作,需要先将数据放入设备缓存区,然后后向地址 0xB3000020 处写入 1 来启动操作,并从 0xB3000030 处获取写磁盘操作的返回值。)
// diskno: disk的ID; secno: 起始扇区号; dst: 从磁盘读取的数据存储目标位置; nsecs: 读取的扇区数量 |
文件系统
思考题
5.1
如果通过 kseg0 读写设备,那么对于设备的写入会缓存到 Cache 中。这是一种错误的行为,在实际编写代码的时候这么做会引发不可预知的问题。请思考:这么做这会引发什么问题?对于不同种类的设备(如我们提到的串口设备和 IDE 磁盘)的操作会有差异吗?可以从缓存的性质和缓存更新的策略来考虑。
Cache 缓存采用的是写回(Write-Back)策略。当 CPU 需要写入数据时,Cache 缓存会将数据先写入 Cache 缓存中,并标记该数据已修改。当 Cache 缓存需要淘汰该数据时,才将数据写回到内存中。
引发的问题:1. 如果对设备控制寄存器进行了缓存,那么第一次引用的时候就把它放入了缓存,以后再对它的引用都是从高速缓存中取值。例如在设备读取操作中,当程序从设备读取数据时,若该数据已存在于Cache中,程序会直接从Cache读取,此时若设备的数据被其他程序或设备修改,就会导致数据不一致。2. 如果程序意外崩溃或者系统意外重启,Cache中的数据可能会丢失,导致设备数据丢失。
不同种类的设备:串口设备(Serial Port Device)是一种通过串行通信接口与计算机进行数据传输的外设设备。串口通信是一种基于一根数据线和一根时钟线进行数据传输的通信方式,它可以实现两台设备之间的数据传输和通信。串口设备传输速率较低,以字符为单位进行读写,更容易引发问题。而IDE磁盘出现错误的可能性较小。
5.2
查找代码中的相关定义,试回答一个磁盘块中最多能存储多少个文件控制块?一个目录下最多能有多少个文件?我们的文件系统支持的单个文件最大为多大?
一个磁盘块大小为 4KB,一个文件控制块大小为256B,则一个磁盘块最多能存储16个文件控制块。文件系统支持的单个文件最大为4MB。一个目录下最多有1024个磁盘块,可以存储4MB内容,最多有16K个文件。
5.3
请思考,在满足磁盘块缓存的设计的前提下,我们实验使用的内核支持的最大磁盘大小是多少?
5.4
在本实验中,fs/serv.h、user/include/fs.h 等文件中出现了许多宏定义,试列举你认为较为重要的宏定义,同时进行解释,并描述其主要应用之处。
fs/serv,h PTE_DIRTY
: 标记缓存在虚拟内存空间的磁盘块是否被修改。
fs.h FILE2BLK (BY2BLK / sizeof(struct File))
: 一个磁盘块可以存储的文件结构数。
fs.h BY2BLK
: 一个磁盘块大小
fs.h BIT2BLK
: 空闲磁盘块的位图标记
fs.h NINDIRECT
: 文件指向的间接磁盘块中块指针的数量
fs.h MAXFILESIZE
: 文件最大大小
fs.h BY2FILE
: 文件结构体的大小
5.5
在 Lab4“系统调用与 fork”的实验中我们实现了极为重要的 fork 函数。那么 fork 前后的父子进程是否会共享文件描述符和定位指针呢?请在完成上述练习的基础上编写一个程序进行验证。
直接对 test lab=5_4 进行修改:
|
结果如下:
说明父子进程共享文件描述符和定位指针。
5.6
请解释 File, Fd, Filefd 结构体及其各个域的作用。比如各个结构体会在哪些过程中被使用,是否对应磁盘上的物理实体还是单纯的内存数据等。说明形式自定,要求简洁明了,可大致勾勒出文件系统数据结构与物理实体的对应关系与设计框架。
// 文件控制块:文件系统服务进程,包括磁盘驱动程序和文件系统的基本函数等,对应磁盘上的物理实体 |
5.7
图5.7中有多种不同形式的箭头,请解释这些不同箭头的差别,并思考我们的操作系统是如何实现对应类型的进程间通信的。
实现箭头表示调用另一个程序中的函数,虚线表示获取调用返回值。
通过 ipc_send()
课上
lab5-2-exam 卡在了 openat 函数忘记 fd_alloc ,等 de 完这个 bug 后来不及写 extra 了。