uCore-Tutorial-Guide-2023S/source/chapter7/3exercise.rst

170 lines
7.9 KiB
ReStructuredText
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

chapter6练习
===========================================
- 本节难度: **大魔王!!**
本章任务
------------------------------------------
- ``ch6b_usertest`` ``ch6_mergetest``
- 结合代码、指导书已经课堂所学,理解文件系统的几个概念:全局文件表 / 进程文件描述符等
- 完成本章编程作业。
- 最终,完成实验报告并 push 你的 ch6 分支到远程仓库。
编程作业
-------------------------------------------
进程通信:邮箱
+++++++++++++++++++++++++++++++++++++++++++
这一章我们实现了基于 pipe 的进程间通信,但是看测例就知道了,管道不太自由,我们来实现一套乍一看更靠谱的通信 syscall吧本节要求实现邮箱机制以及对应的 syscall。
- 邮箱说明每个进程拥有唯一一个邮箱基于“数据报”收发字节信息利用环形buffer存储读写顺序为 FIFO不记录来源进程。每次读写单位必须为一个报文如果用于接收的缓冲区长度不够舍弃超出的部分截断报文。为了简单邮箱中最多拥有16条报文每条报文最大长度256字节。当邮箱满时发送邮件也就是写邮箱会失败。不考虑读写邮箱的权限也就是所有进程都能够随意给其他进程的邮箱发报。
**mailread**:
* syscall ID401
* C接口 ``int mailread(void* buf, int len)``
* 功能:读取一个报文,如果成功返回报文长度.
* 参数:
* buf: 缓冲区头。
* len缓冲区长度。
* 说明:
* len > 256 按 256 处理len < 队首报文长度且不为0则截断报文。
* len = 0则不进行读取如果没有报文读取返回-1否则返回0这是用来测试是否有报文可读。
* 可能的错误:
* 邮箱空。
* buf 无效。
**mailwrite**:
* syscall ID402
* C接口 ``int mailwrite(int pid, void* buf, int len)``
* 功能:向对应进程邮箱插入一条报文.
* 参数:
* pid: 目标进程id。
* buf: 缓冲区头。
* len缓冲区长度。
* 说明:
* len > 256 按 256 处理,
* len = 0则不进行写入如果邮箱满返回-1否则返回0这是用来测试是否可以发报。
* 可以向自己的邮箱写入报文。
* 可能的错误:
* 邮箱满。
* buf 无效。
实现完成之后,你应该能通过 ch6_mail* 对应的所有测例,在 shell 中执行 ch6_usertest 来执行所有测试。
tips:
- 给每个进程默认分配一个邮箱即可。
- 邮箱的具体实现就是一个 ring buffer。
[挑战,不占分]进程通信:共享内存
+++++++++++++++++++++++++++++++++++++++++++
如果你认为邮箱不够炫酷,可以试试这个。挑战内容,不占分,但你还是必须先实现 mail。
进程间通信(IPC)对于某些系统和应用其实十分重要,它被称为微内核的 Achilles tendon同时在 android 应用中也十分常见,为此 android 系统专门设计了一套 binder 机制来加速 IPC 的效率。
最基础的 IPC 方式大致分两类:
- 内核拷贝:指通过内核完成数据的拷贝,比如 pipe邮箱。
- 共享内存:直接将同一段物理内存映射到不同进程的虚存空间。
其中内核拷贝的方式一般效率较低,但安全可靠,容易同步。而共享内存的方式不需要内核参与,速度较快,但需要用户态自己想办法同步,同时可能会导致某些攻击,感兴趣的同学可以参考 `TOCTTOU <https://en.wikipedia.org/wiki/TOCTTOU>`_
框架实现的 pipe 属于第一类,那么现在我们来实现第二类。实现 share memory 的方法不止一种,这里我们拓展 ch4 实现的 mmap 的功能,利用 mmap 来实现共享内存。
mmap 系统调用新定义:
- syscall ID: 222
- 接口:``int mmap(void* start, unsigned long long len, int port, int flag, int shmem_id)``
- 功能:当 flag 等于 0 时,功能与 ch4 时一致;当 flag = 1 时,视为申请共享内存(可类比 posix 接口的 MAP_SHARED 标志),这时需要根据 shmem_id 将对应的物理内存映射到 start 开始的虚存,内存页的属性为 prot。若此时 shmem_id 为 -1 时,视为需要申请一段新的物理内存作为共享内存使用; 若 shmem_id != -1视作申请对应 id 的共享内存。
- 参数:
- start需要映射的虚存起始地址。
- len映射字节长度可以为 0 (如果是则直接返回),不可过大 (上限 1GiB )。
- port第 0 位表示是否可读,第 1 位表示是否可写,第 2 位表示是否可执行。其他位无效(必须为 0 )。
- flag申请内存的模式为 0 时为申请物理内存,为 1 时为申请共享内存,其他值视作错误。
- shmem_id申请共享内存时使用表示内核记录的一段共享内存的 id该 id 全内核唯一(注意和 fd 的区别fd 是 process 的属性, shmem_id 是全内核的属性)。
- 返回值:
- 若发生错误,返回 -1。
- 若 flag == 0返回值同 ch4。
- 若 flag == 1总返回映射的 shmem_id。
- 说明:
- 我们尚未有完整文件系统,所以这只是一个看上去像 posix 的 mmap 但实际不是的系统调用。
- 我们不定义共享内存与 fork 的相互作用,不会加以测试,任何实现都可以。
- 允许同一个进程将同一块共享内存映射到自己的不同虚存。
- 为了简单addr 要求按页对齐(否则报错)len 可直接按页上取整。
- 为了简单,不考虑发生错误时的页回收(也就是内存泄漏)。
- 错误:
- [addr, addr + len) 存在已经被映射的页。
- 物理内存不足。
- port & !0x7 != 0 (port 其余位必须为0)。
- port & 0x7 = 0 (这样的内存无意义)。
- flag & ~0x1 != 0 (flag 应为 0 或 1)
- shmem_id 无效。
munmap 系统调用定义:
- syscall ID215
- C接口 ``int munmap(void* start, unsigned long long len)``
- Rust接口 ``fn munmap(start: usize, len: usize) -> i32``
- 功能:取消一块虚存的映射。
- 参数:同 mmap
- 说明:
- 为了简单,参数错误时不考虑内存的恢复和回收。
- 错误:
- [start, start + len) 中存在未被映射的虚存。
正确实现后,你的 os 应该能够正确运行 ch6_shmem* 对应的一些测试用例,在 shell 中执行 ch6_usertest1 来执行测试。
tips:
- QAQshmem 的企划被老师以太难为由毙掉了,所以就成了 challange...
- 难受啊,这个唯一一个测例和参考实现都写好了的 challange ...
问答作业
-------------------------------------------
1. 举出使用 pipe 的一个实际应用的例子。
tips:
- 想想你平时咋使用 linux terminal 的?
- 如何使用 cat 和 wc 完成一个文件的行数统计?
2. 共享内存的测例中有如下C语言片段(伪代码)
.. code-block:: c
int main()
{
uint64 *A = (void *)0x10000000;
uint64 *B = (void *)(0x10000000 + 0x1000);
uint64 len = 0x1000;
make_shmem(A, B, len); // 会将 [A, A + len) [B, B + len) 这两段虚存映射到同一段物理内存
*A = 0xabab;
__sync_synchronize(); // zssm
if(*B != 0xabab) {
return ERROR;
}
printf("OK!");
return 0;
}
请自己查阅注释 ``zssm?`` 对应的这一行代码有什么作用?如果去掉有可能会导致什么错误?为什么?
报告要求
-------------------------------
注意目录要求,报告命名 ``lab4.pdf``,位于 ``reports`` 目录下。 后续实验同理。
报告内容:
- 注明姓名学号。
- 简单总结本次实验你新添加的代码。
- 完成 ch6 问答作业。
- [可选,不占分]你对本次实验设计及难度的看法。