diff --git a/source/chapter2/5exercise.rst b/source/chapter2/5exercise.rst index a47f6c513f41c1d96355deca1a108f87854e06f7..10b4c18826083c4d2ab395aca3fd1a44707df52a 100644 --- a/source/chapter2/5exercise.rst +++ b/source/chapter2/5exercise.rst @@ -109,6 +109,7 @@ lab2 中,我们实现了第一个系统调用 ``sys_write``,这使得我们 3. 描述程序陷入内核的两大原因是中断和异常,请问 riscv64 支持那些中断/异常?如何判断进入内核是由于中断还是异常?描述陷入内核时的几个重要寄存器及其值。 +4. 对于任何中断, ``__alltraps`` 中都需要保存所有寄存器吗?你有没有想到一些加速 ``__alltraps`` 的方法?简单描述你的想法。 报告要求 ------------------------------- diff --git a/source/chapter5/4exercise.rst b/source/chapter5/4exercise.rst new file mode 100644 index 0000000000000000000000000000000000000000..95abcea4cfb7942c08b07778d4e8e446f5d9e3f6 --- /dev/null +++ b/source/chapter5/4exercise.rst @@ -0,0 +1,74 @@ +chapter5 练习 +============================================== + +- 本节难度: **一定比lab4简单** + +编程作业 +--------------------------------------------- + +进程创建 ++++++++++++++++++++++++++++++++++++++++++++++ + +大家一定好奇过为啥进程创建要用 fork + execve 这么一个奇怪的系统调用,就不能直接搞一个新进程吗?思而不学则殆,我们就来试一试!这章的编程练习请大家实现一个完全 DIY 的系统调用 spawn,用以创建一个新进程。 + +spawn 系统调用定义: + +- syscall ID: 400 +- C 接口: ``int spawn(char *filename)`` +- Rust 接口: ``fn spawn(file: *const u8) -> isize`` +- 功能:相当于 fork + exec,新建子进程并执行目标程序。 +- 说明:成功返回子进程id,否则返回 -1。 +- 可能的错误: + - 无效的文件名。 + - 进程池满/内存不足等资源错误。 + +实验要求 ++++++++++++++++++++++++++++++++++++++++++++++ +- 实现分支:ch5。 +- 完成实验指导书中的内容,实现进程控制,可以运行 usershell。 +- 实现自定义系统调用 spawn,并通过 并通过 `Rust测例 `_ 中chapter5对应的所有测例。 + +challenge: 支持多核。 + +实验检查 ++++++++++++++++++++++++++++++++++++++++++++++ + +- 实验目录要求 + + 目录要求不变(参考lab1目录或者示例代码目录结构)。同样在 os 目录下 ``make run`` 之后可以正确加载用户程序并执行。 + 加载的用户测例位置: ``../user/build/bin``。 + +- 检查 + + 可以正确 ``make run`` 执行,可以正确执行目标用户测例,并得到预期输出(详见测例注释)。 + +问答作业 +-------------------------------------------- + +(1) fork + exec 的一个比较大的问题是 fork 之后的内存页/文件等资源完全没有使用就废弃了,针对这一点,有什么改进策略? + +(2) 其实使用了题(1)的策略之后,fork + exec 所带来的无效资源的问题已经基本被解决了,但是今年来 fork 还是在被不断的批判,那么到底是什么正在"杀死"fork?可以参考 `论文 `_ ,**注意**:回答无明显错误就给满分,出这题只是想引发大家的思考,完全不要求看论文,球球了,别卷了。 + +(3) fork 当年被设计并称道肯定是有其好处的。请使用 **带初始参数** 的 spawn 重写如下 fork 程序,然后描述 fork 有那些好处。注意:使用"伪代码"传达意思即可,spawn接口可以自定义。可以写多个文件。 + + .. code-block:: rust + + fn main() { + let a = get_a(); + if fork() == 0 { + let b = get_b(); + println!("a + b = {}", a + b); + exit(0); + } + println!("a = {}", a); + 0 + } + +4. 描述进程执行的几种状态,以及 fork/exec/wait/exit 对与状态的影响。 + +报告要求 +------------------------------------------------------------ + +* 简单总结本次实验与上个实验相比你增加的东西。(控制在5行以内,不要贴代码) +* 完成问答问题 +* (optional) 你对本次实验设计及难度的看法。 \ No newline at end of file diff --git a/source/chapter5/index.rst b/source/chapter5/index.rst index 2100bfd1a9bcd08ee0a3707fadb442871b7faf3a..73868ceaab585bf218c5454fb9e9eef834bd4e64 100644 --- a/source/chapter5/index.rst +++ b/source/chapter5/index.rst @@ -8,6 +8,7 @@ 1process 2core-data-structures 3implement-process-mechanism + 4exercise MULTICS操作系统是侏罗纪的“霸王龙”操作系统。 UNIX操作系统是小巧聪明的“伤齿龙”操作系统。 \ No newline at end of file diff --git a/source/chapter6/3exercise.rst b/source/chapter6/3exercise.rst new file mode 100644 index 0000000000000000000000000000000000000000..e79ea49a7295ce426c0b0b2e6160891ac0fb7960 --- /dev/null +++ b/source/chapter6/3exercise.rst @@ -0,0 +1,89 @@ +chapter6 练习 +=========================================== + +- 本节难度: **也就和lab3一样吧** + +编程作业 +------------------------------------------- + +进程通信:邮件 ++++++++++++++++++++++++++++++++++++++++++++ + +这一章我们实现了基于 pipe 的进程间通信,但是看测例就知道了,管道不太自由,我们来实现一套乍一看更靠谱的通信 syscall吧!本节要求实现邮箱机制,以及对应的 syscall。 + +- 邮箱说明:每个进程拥有唯一一个邮箱,基于“数据报”收发字节信息,利用环形buffer存储,读写顺序为 FIFO,不记录来源进程。每次读写单位必须为一个报文,如果用于接收的缓冲区长度不够,舍弃超出的部分(截断报文)。为了简单,邮箱中最多拥有16条报文,每条报文最大长度256字节。当邮箱满时,发送邮件(也就是写邮箱会失败)。不考虑读写邮箱的权限,也就是所有进程都能够随意给其他进程的邮箱发报。 + +**mailread**: + + * syscall ID:401 + * C接口: ``int mailread(void* buf, int len)`` + * Rust接口: ``fn mailread(buf: *mut u8, len: usize)`` + * 功能:读取一个报文,如果成功返回报文长度. + * 参数: + * buf: 缓冲区头。 + * len:缓冲区长度。 + * 说明: + * len > 256 按 256 处理,len < 队首报文长度且不为0,则截断报文。 + * len = 0,则不进行读取,如果没有报文读取,返回-1,否则返回0,这是用来测试是否有报文可读。 + * 可能的错误: + * 邮箱空。 + * buf 无效。 + +**mailwrite**: + + * syscall ID:402 + * C接口: ``int mailwrite(int pid, void* buf, int len)`` + * Rust接口: ``fn mailwrite(pid: usize, buf: *mut u8, len: usize)`` + * 功能:向对应进程邮箱插入一条报文. + * 参数: + * pid: 目标进程id。 + * buf: 缓冲区头。 + * len:缓冲区长度。 + * 说明: + * len > 256 按 256 处理, + * len = 0,则不进行写入,如果邮箱满,返回-1,否则返回0,这是用来测试是否可以发报。 + * 可以向自己的邮箱写入报文。 + * 可能的错误: + * 邮箱满。 + * buf 无效。 + +实验要求 ++++++++++++++++++++++++++++++++++++++++++++++ + +- 实现分支:ch6。 +- 完成实验指导书中的内容,实现进程控制,可以基于 pipe 进行进程通信。 +- 实现邮箱机制及系统调用,并通过 `Rust测例 `_ 中 chapter6 对应的所有测例。 + +challenge: 支持多核。 + +实验检查 +++++++++++++++++++++++++++++++++++++++++++++++ + +- 实验目录要求 + + 目录要求不变(参考lab1目录或者示例代码目录结构)。同样在 os 目录下 ``make run`` 之后可以正确加载用户程序并执行。 + + 加载的用户测例位置: ``../user/build/bin``。 + +- 检查 + + 可以正确 ``make run`` 执行,可以正确执行目标用户测例,并得到预期输出(详见测例注释)。 + +问答作业 +------------------------------------------- + +(1) 举出使用 pipe 的一个实际应用的例子。 + +(2) 假设我们的邮箱现在有了更加强大的功能,容量大幅增加而且记录邮件来源,可以实现“回信”。考虑一个多核场景,有 m 个核为消费者,n 个为生产者,消费者通过邮箱向生产者提出订单,生产者通过邮箱回信给出产品。 + + - 假设你的邮箱实现没有使用锁等机制进行保护,在多核情景下可能会发生那些问题?单核一定不会发生问题吗?为什么? + - 请结合你在课堂上学到的内容,描述读者写者问题的经典解决方案,必要时提供伪代码。 + - 由于读写是基于报文的,不是随机读写,你有什么点子来优化邮箱的实现吗? + + +报告要求 +--------------------------------------- + +* 简单总结本次实验与上个实验相比你增加的东西。(控制在5行以内,不要贴代码) +* 完成问答问题 +* (optional) 你对本次实验设计及难度的看法。 \ No newline at end of file diff --git a/source/chapter6/index.rst b/source/chapter6/index.rst index 81e177e8319c206fb9239ccc96c4cd2f70bd5281..9cef196b0e8027fb93d953c97247b43dab6920e6 100644 --- a/source/chapter6/index.rst +++ b/source/chapter6/index.rst @@ -7,5 +7,6 @@ 0intro 1file-descriptor 2pipe + 3exercise 有团队协作能力的“迅猛龙”操作系统。 \ No newline at end of file diff --git a/source/chapter7/6exercise.rst b/source/chapter7/6exercise.rst new file mode 100644 index 0000000000000000000000000000000000000000..3ed8a45bb5e97ac6fc9ca86e7bc1aa180c624935 --- /dev/null +++ b/source/chapter7/6exercise.rst @@ -0,0 +1,121 @@ +lab7 实验要求 +================================================ + +- 本节难度: **综合难度较大,编程难度小** + +编程作业 +------------------------------------------------- + +硬链接 +++++++++++++++++++++++++++++++++++++++++++++++++++ + +你的电脑桌面是咋样的?是放满了图标吗?反正我的 windows 是这样的。显然很少人会真的吧可执行文件放到桌面上,桌面图标其实都是一些快捷方式。或者用 unix 的术语来说:软链接。为了减少工作量,我们今天来实现软链接的兄弟:[硬链接](https://en.wikipedia.org/wiki/Hard_link)。 + +硬链接要求两个不同的目录项指向同一个文件,在我们的文件系统中也就是两个不同名称目录项指向同一个磁盘块。本节要求实现三个系统调用 sys_linkat、sys_unlinkat、sys_stat。 + +**linkat**: + + * syscall ID: 37 + * 功能:创建一个文件的一个硬链接, `linkat标准接口 `_ 。 + * C接口: ``int linkat(int olddirfd, char* oldpath, int newdirfd, char* newpath, unsigned int flags)`` + * Rust 接口: ``fn linkat(olddirfd: i32, oldpath: *const u8, newdirfd: i32, newpath: *const u8, flags: u32) -> i32`` + * 参数: + * olddirfd,newdirfd: 仅为了兼容性考虑,本次实验中始终为 AT_FDCWD (-100),可以忽略。 + * flags: 仅为了兼容性考虑,本次实验中始终为 0,可以忽略。 + * oldpath:原有文件路径 + * newpath: 新的链接文件路径。 + * 说明: + * 为了方便,不考虑新文件路径已经存在的情况(属于未定义行为),除非链接同名文件。 + * 返回值:如果出现了错误则返回 -1,否则返回 0。 + * 可能的错误 + * 链接同名文件。 + +**unlinkat**: + + * syscall ID: 35 + * 功能:取消一个文件路径到文件的链接, `unlinkat标准接口 `_ 。 + * C接口: ``int unlinkat(int dirfd, char* path, unsigned int flags)`` + * Rust 接口: ``fn unlinkat(dirfd: i32, path: *const u8, flags: u32) -> i32`` + * 参数: + * dirfd: 仅为了兼容性考虑,本次实验中始终为 AT_FDCWD (-100),可以忽略。 + * flags: 仅为了兼容性考虑,本次实验中始终为 0,可以忽略。 + * path:文件路径。 + * 说明: + * 为了方便,不考虑使用 unlink 彻底删除文件的情况。 + * 返回值:如果出现了错误则返回 -1,否则返回 0。 + * 可能的错误 + * 文件不存在。 + +**fstat**: + + * syscall ID: 80 + * 功能:获取文件状态。 + * C接口: ``int fstat(int fd, struct Stat* st)`` + * Rust 接口: ``fn fstat(fd: i32, st: *mut Stat) -> i32`` + * 参数: + * fd: 文件描述符 + * st: 文件状态结构体 + + .. code-block:: rust + + #[repr(C)] + #[derive(Debug)] + pub struct Stat { + /// 文件所在磁盘驱动器号 + pub dev: u64, + /// inode 文件所在 inode 编号 + pub ino: u64, + /// 文件类型 + pub mode: StatMode, + /// 硬链接数量,初始为1 + pub nlink: u32, + /// 无需考虑,为了兼容性设计 + pad: [u64; 7], + } + + /// StatMode 定义: + bitflags! { + pub struct StatMode: u32 { + const NULL = 0; + /// directory + const DIR = 0o040000; + /// ordinary regular file + const FILE = 0o100000; + } + } + + +实验要求 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +- 实现分支:ch7。 +- 完成实验指导书中的内容,实现基本的文件操作。 +- 实现硬链接及相关系统调用,并通过 `Rust测例 `_ 中 chapter7 对应的所有测例。 + +challenge: 支持多核。 + +实验检查 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +- 实验目录要求 + + 目录要求不变(参考lab1目录或者示例代码目录结构)。同样在 os 目录下 `make run` 之后可以正确加载用户程序并执行。 + + 加载的用户测例位置: `../user/build/bin`。 + +- 检查 + + 可以正确 `make run` 执行,可以正确执行目标用户测例,并得到预期输出(详见测例注释)。 + +问答作业 +---------------------------------------------------------- + +1. 目前的文件系统只有单级目录,假设想要支持多级文件目录,请描述你设想的实现方式,描述合理即可。 + +2. 在有了多级目录之后,我们就也可以为一个目录增加硬链接了。在这种情况下,文件树中是否可能出现环路?你认为应该如何解决?请在你喜欢的系统上实现一个环路,描述你的实现方式以及系统提示、实际测试结果。 + +报告要求 +----------------------------------------------------------- +* 简单总结本次实验与上个实验相比你增加的东西。(控制在5行以内,不要贴代码) +* 完成问答问题 +* (optional) 你对本次实验设计及难度的看法。 \ No newline at end of file diff --git a/source/chapter7/index.rst b/source/chapter7/index.rst index bf1508ea3960af34dc15fa2e6cd1444f9a63f6a5..e8ac27c9ec819f6575387f33d6a9bb295385530a 100644 --- a/source/chapter7/index.rst +++ b/source/chapter7/index.rst @@ -5,4 +5,6 @@ :hidden: :maxdepth: 4 + 6exercise + 最晚灭绝的“霸王龙”操作系统 \ No newline at end of file