# Lab8实验报告

## 练习1

### 完成读文件操作的实现

在实现过程中，主要修改的是`sfs_io_nolock`的代码，该函数的功能是给定一个文件的`inode`以及需要读写的偏移量和大小，转换成数据块级别的读写操作。主要调用的是两个函数：

- `sfs_bmap_load_nolock`: 该函数能够将文件数据块的便宜转换成硬盘空间块的数据块编号；
- `sfs_block_op` / `sfs_buff_op`：该函数接受硬盘空间数据块编号，并进行读写操作；

当然，用户希望读取的文件大小和偏移并非是和数据块一一对齐的，因此在边界情况的时候需要特殊处理，这也是这个实验的要点所在，需要判断偏移的开头未对齐部分和结尾未对齐部分专门进行处理，而中间部分则调用对齐的`block`级别操作进行处理。

实际上，跟踪函数`sfs_buff_op`的调用可以得知：该函数在进行设备操作的时候也是以块为单位的。如果要读取一个块内的小部分数据，就必须将数据块整体读出或整体写入。这与原理相关的部分也是契合的。

### 实现“UNIX的PIPE机制”的概要设方案

管道机制是UNIX进程间通讯的重要机制，为了简便实现，我计划直接将管道文件映射到一块内存区域，每次在文件系统清理的时候就将管道清空，符合管道机制的语义。

为了创建管道，与UNIX系统相同，需要增加相关的系统调用（例如`SYS_pipe`），此后就可以统一的使用`write`和`read`接口进行读和写操作了。具体到uCore的实现，我认为可以在`sfs_inode`数据结构中增加特殊的标记位确定是不是管道文件，同时增加两个信号量（由于之前已经有一个互斥锁，就不需要再多余使用一个锁了）用于进行类似“生产者-消费者”问题的同步互斥控制。

## 练习2

### 完成基于文件系统的执行程序机制的实现

在实现过程中，主要修改的是`load_icode`函数，其他函数例如`init_proc`和`do_fork`函数也需要进行少许的修改。

相比Lab7，`load_icode`函数的参数有了较大的变化：首先，读取ELF文件不再从内存中读取，而是通过已经实现好的文件系统的`read`操作进行硬盘文件读取；其次，加入了任意大小参数`argc`和`argv`的功能，使得应用程序能够接受命令行参数输入。

针对ELF文件的硬盘读取方式，主要是通过调用`load_icode_read`函数完成，该函数又在文件系统之上调用了`read`和`seek`函数，基于SFS对文件进行读取和寻址操作，在实现的时候，需要依次将ELF头，`program header`以及真正的各个代码段数据段读入内存。

而对于命令行参数的实现，则是直接将相关的字符串拷贝到用户栈的顶端，并将最开始的栈指针放在`argc`的起始地址即可。需要注意的是，用户态的`initcode.S`和`umain.c`也需要做相应的改动，为了能够使得参数顺利传送到`main`函数中，需要将参数寄存器设置为`argc`和`argv`的地址。

### 实现基于“UNIX的硬链接和软链接机制”的概要设方案

**硬链接**：在SFS文件系统中已经实现了`nlinks`数据结构，代表了指向这个`inode`的硬链接个数，因此只需要添加一个系统调用（例如`SYS_link`），该系统调用首先找到被链接文件对应的`inode`，然后在目标文件夹的控制块中增加一个描述符即可，二者的`inode`指针应该相同，同时`nlinks`数据结构应该相应增加。

**软链接**：软链接的实现稍微复杂些，需要在`inode`上增加标记位确认这个文件是普通文件还是软链接文件，在进行打开文件或是保存文件的时候，操作系统需要根据软链接指向的地址再次在文件目录中进行查询，寻找或创建相应的`inode`，注意与硬链接不同，创建软链接的时候不涉及对`nlinks`的修改。如果需要创建软链接这个特殊的文件，也需要增加一个系统调用（例如`SYS_symlink`）在完成相应的功能。

## 实现中与参考答案的区别

- 练习1：基本思路与伪代码相同，都是对于三种情况组合的特殊处理，某些细节部分有所不同，但大体相似；
- 练习2：在处理`argc`和`argv`的时候先存储了所有`argv`指向字符串的地址，不需要两次统计字符串的长度，效率略有提升。

## 实验中的重要知识点

本次实验中重要的知识点有：

- 文件系统和文件
- 文件描述符
- 文件、目录的定义与区别
- 虚拟文件系统对上层提供的接口以及实现

而OS原理中很重要，但在实验中没有对应上的知识点有：

- 冗余磁盘阵列RAID
- 文件分配
- I/O子系统的相关控制
