diff --git a/README.md b/README.md
deleted file mode 100644
index 6fe3cd73d82564047b2983defc42e5e381ce6b16..0000000000000000000000000000000000000000
--- a/README.md
+++ /dev/null
@@ -1,124 +0,0 @@
-# Welcome to NPUcore👋
-
-[![Lang](https://img.shields.io/badge/Lang-Rust-green)](https://img.shields.io/badge/Lang-Rust-green) [![ISA](https://img.shields.io/badge/ISA-riscv64-yellowgreen)](https://img.shields.io/badge/ISA-riscv64-yellowgreen) [![Platform](https://img.shields.io/badge/Platform-Qemu%2c%20k210%20%26%20Hifive%20Unmatched-yellow)](https://img.shields.io/badge/Platform-Qemu%2c%20k210%20%26%20Hifive%20Unmatched-yellow)
-
-### 简介
-
-`NPUcore`是来自西北工业大学的三位同学基于清华大学`rCore Tutorial`框架,参考借鉴去年内核赛道优秀参赛队伍与Linux内核的诸多优秀设计,并结合硬件平台特性进行优化和重构,不断迭代形成的竞赛操作系统,现已支持`qemu`、`k210`、`Hifive Unmatched`三平台的运行。
-
-### 特性
-
-+ 完善的内核功能支持
-
-`NPUcore`根据Linux Manual支持或不完全支持的系统调用多达87个,且较为完整地实现了信号机制,线程等功能,在决赛第一阶段针对C库接口的测试程序`libc-test`中获得了满分。但我们的目标并不仅仅是通过测试用例,而是希望`NPUcore`具有一定的趣味性和实用性,这就要求`NPUcore`能够运行更多用户程序。因此我们支持了bash这个许多Linux发行版的默认shell,结合busybox可以获得较好的CLI使用体验。
-
-+ 精巧的内存管理
-
-`NPUcore`最初面向`k210`平台,由于`k210`只有8MB内存空间,有限的空间驱使`NPUcore`团队围绕内存管理做了很多工作:懒分配、写时复制、zRAM,虚拟内存等。这些机制使得内存能够得到尽可能的利用,能够支撑更多用户程序。
-
-+ 基于等待队列的阻塞
-
-阻塞与非阻塞是资源访问的两种方式。在非阻塞的资源访问下,通常要使用轮询来检查待获取的资源是否就绪,这种反复查询的操作会造成CPU资源的浪费。`NPUCore`实现了基于等待队列的系统资源访问,`futex`等系统调用使用等待队列实现,能够提高内核事件通知和资源访问的效率。
-
-+ 块缓存和页缓存
-
-`NPUcore`团队通过对函数调用的跟踪统计发现文件系统在I/O时的等待有极大的开销,为了提高系统I/O性能, `NPUcore`实现了类似Linux的Buffer Cache(块缓存)和Page Cache(页缓存),Buffer Cache缓存块设备上的数据块,加速对存储器的访问。Page Cache缓存文件的逻辑内容, 加速对文件内容的访问。此外,考虑到`k210`的内存有限,而Buffer Cache和Page Cache之间又存在数据冗余,`NPUcore`尝试将Buffer Cache和Page Cache之间重复的数据映射到相同的物理页,并且进行维护,确保不会出现一致性问题。这样能够提升内存利用率,减少Buffer Cache和Page Cache之间的拷贝。此外,我们还根据Fat区和数据区的特性设计了不同的缓存策略,使得文件系统的I/O性能大幅上升。
-
-`NPUcore`使用较为激进的缓存策略,Page Cache容量不设上限,所有的内存空间都可以作为缓存使用。发生内存不足时,`NPUcore` 会根据LRU算法清理无用缓存。经过测试发现,在这种缓存策略下运行大多数测例时,对每个文件`NPUcore`只从外存读取一次,之后的读写全部发生在Cache中,从而带来极大的性能提升。
-
-+ 高效可扩展的虚拟文件系统
-
-在前期开发中,`NPUCore`团队注意到文件目录的查找操作每次都需要访问底层文件系统,反复读取Fat区和数据区,造成了巨大的耗时。`NPUcore`在开发过程中对虚拟文件系统进行了重写,引入了目录树这一数据结构,作为文件目录逻辑结构的缓存,一些文件系统操作如切换目录等在缓存命中时不再需要访问底层文件系统,大幅减少文件I/O,使文件操作更加高效。
-
-另外,在Linux的VFS中,同一文件操作可以通过函数指针实现对不同底层文件系统的函数调用,这其实是一种由C语言实现的“多态”。我们借鉴这种思想,使用Rust的语法将文件操作trait化,这样能够让VFS具有更高的可扩展性。
-
-+ 稳定性
-
-`NPUcore`遵循软件工程的思想,在每次合并新模块后都会进行压力测试和回归测试,能够尽可能发现系统中隐藏的bug。结合完善的运行日志系统,QEMU远程调试和JTAG硬件调试,可以迅速定位问题并加以解决。这使得`NPUcore`有相当的稳定性,能够在`k210`开发板上循环运行决赛测例长达6小时。
-
-### 决赛第一阶段完成情况
-
-`NPUcore`在决赛第一阶段完成了`libc-test`的所有测试用例的支持,拿到了`k210`赛道220分满分的成绩,同时也支持了`fu740`平台,在`Unmatched`赛道拿到了满分,成为了为数不多的支持双赛道的队伍之一。
-
-##### k210赛道
-
-![k210](./Doc/pic/k210.png)
-##### unmatched赛道
-
-![unmatched](./Doc/pic/unmatched.png)
-
-### 分支介绍
-
-- master——最新代码及文档
-- comp-1——初赛提交分支
-- comp-2——决赛一阶段提交分支(k210)
-- comp-2-qemu——决赛一阶段提交分支(qemu)
-- comp-2-fu740——决赛一阶段提交分支(fu740)
-- comp-3——决赛二阶段提交分支(k210)
-- comp-3-qemu——决赛二阶段提交分支(qemu)
-- comp-3-fu740——决赛二阶段提交分支(fu740)
-- extend——扩展内容
-
-### 如何运行
-
-**交互式运行**
-
-准备工作
-
-````shell
-make all # 构建`bash`与`initproc`
-cd os && make fat32 # 进入`os`目录,构建文件系统镜像
-````
-
-在QEMU上运行:
-
-````shell
-make run # 直接运行
-````
-
-在k210上运行:
-
-````shell
-make sdcard # 将文件系统镜像写入sdcard
-make run BOARD=k210 # 烧写运行
-````
-
-PS:
-
-1. 在OS运行时直接关闭模拟器或者拔下k210有概率写坏文件系统,如果遇到这种情况,请重新构建文件系统镜像(将文件系统镜像重新写入sdcard)。
-2. 按以上步骤构建的文件系统镜像仅含`initproc`和`bash`,如果需要使用更多用户程序,请修改`buildfs.sh`或者手动将ELF文件拷贝到镜像中。
-
-### 模块与特性相关文档
-
-+ [信号](./Doc/os/signal.md)
-+ [oom handler](./Doc/os/oom.md)
-+ [Page Fault & CoW](./Doc/mm/page_fault.md)
-+ [futex系统调用](./Doc/task/futex.md)
-+ [修改RustSBI处理不对齐读](./Doc/debug/rustsbi%E4%B8%8D%E5%AF%B9%E9%BD%90%E8%AF%BB.md)
-+ [常数优化](./Doc/debug/常数优化相关方法.md)
-
-### Fat32文件系统教程
-
-`NPUcore`在开发文件系统的同时记录了开发的心路历程,整理成了一份教程。`NPUcore`团队的理念是:对的方式千篇一律,错的方式千奇百怪,为什么是错的比什么是对的更加重要。所以这份教材会记录我们走过的弯路,希望能够为那些想从零开始写Fat32文件系统的人提供一些帮助。
-
-+ [来自NPUcore的Fat32文件系统教程](./Doc/fs/fat.md)
-+ [Linux下的短目录名生成](./Doc/fs/Linux%E4%B8%8B%E7%9A%84%E7%9F%AD%E7%9B%AE%E5%BD%95%E5%90%8D%E7%94%9F%E6%88%90.md)
-+ [代码文档: easy-fs-doc](./Doc/fs/easy-fs-doc.md)
-
-### Debug相关文档
-
-以下是我们在开发中遇到的各种bug和解决方法。
-
-+ [Virt-IO驱动跨页问题](./Doc/debug/Virt-IO%E9%A9%B1%E5%8A%A8bug.md)
-+ [浮点寄存器的保存](./Doc/debug/%E6%B5%AE%E7%82%B9%E5%AF%84%E5%AD%98%E5%99%A8bug.md)
-+ [清理页缓存导致死锁](./Doc/debug/%E6%B8%85%E7%90%86%E9%A1%B5%E7%BC%93%E5%AD%98%E6%AD%BB%E9%94%81.md)
-+ [read/write错误返回EFAULT](./Doc/debug/EFAULT技术细节.md)
-+ [vi不能输入](./Doc/debug/vi不能输入bug.md)
-+ [vi返回异常](./Doc/debug/vi返回异常bug.md)
-
-### 其他技术细节
-
-+ [页表项刷新相关细节](./Doc/debug/%E9%A1%B5%E8%A1%A8%E9%A1%B9%E5%88%B7%E6%96%B0%E7%9B%B8%E5%85%B3%E7%BB%86%E8%8A%82.md)
-+ [Rust的传锁方法](./Doc/debug/Rust%E7%9A%84%E4%BC%A0%E9%94%81%E6%96%B9%E6%B3%95.md)
-+ [提升SD卡驱动稳定性](./Doc/debug/sd%E5%8D%A1%E9%A9%B1%E5%8A%A8%E5%93%8D%E5%BA%94%E5%A4%B1%E8%B4%A5bug.md)
-+ [TTY回显问题](./Doc/debug/TTY不回显bug.md)
diff --git a/os/src/syscall/process.rs b/os/src/syscall/process.rs
index 6649f99cedebed02de2deba58ca085eb52b60bc2..28a0a2cd7a8683d71150f2754cc549ee4af2207f 100644
--- a/os/src/syscall/process.rs
+++ b/os/src/syscall/process.rs
@@ -495,7 +495,7 @@ pub fn sys_clone(
 // }
 
 pub fn sys_fork(stack: *const u8, ptid: *mut u32, tls: usize, ctid: *mut u32) -> isize {
-    sys_clone(28, stack, ptid, tls, ctid)
+    sys_clone(17, stack, ptid, tls, ctid)
 }
 
 pub fn sys_execve(
diff --git "a/\346\226\207\346\241\243/image/QQ20241223-204809.png" "b/\346\226\207\346\241\243/image/QQ20241223-204809.png"
new file mode 100644
index 0000000000000000000000000000000000000000..004fe1a176772da191337dd6eaa71a5e52bdb415
Binary files /dev/null and "b/\346\226\207\346\241\243/image/QQ20241223-204809.png" differ
diff --git "a/\346\226\207\346\241\243/image/QQ20241223-204848.png" "b/\346\226\207\346\241\243/image/QQ20241223-204848.png"
new file mode 100644
index 0000000000000000000000000000000000000000..fdc4f891f4625d286f2fdba3824de149f2bc128b
Binary files /dev/null and "b/\346\226\207\346\241\243/image/QQ20241223-204848.png" differ
diff --git "a/\346\226\207\346\241\243/image/QQ20241223-204951.png" "b/\346\226\207\346\241\243/image/QQ20241223-204951.png"
new file mode 100644
index 0000000000000000000000000000000000000000..9a7271635563b2dcea4a794f4d7e873ba2de9747
Binary files /dev/null and "b/\346\226\207\346\241\243/image/QQ20241223-204951.png" differ
diff --git "a/\346\226\207\346\241\243/image/QQ20241223-210122.png" "b/\346\226\207\346\241\243/image/QQ20241223-210122.png"
new file mode 100644
index 0000000000000000000000000000000000000000..a5cb58735f62e704128957b49734f60d90c1e2c7
Binary files /dev/null and "b/\346\226\207\346\241\243/image/QQ20241223-210122.png" differ
diff --git "a/\346\226\207\346\241\243/image/QQ20241223-210809.png" "b/\346\226\207\346\241\243/image/QQ20241223-210809.png"
new file mode 100644
index 0000000000000000000000000000000000000000..0800dd9a5284f0565fe0f1e3ffe6076bf5f677dd
Binary files /dev/null and "b/\346\226\207\346\241\243/image/QQ20241223-210809.png" differ
diff --git "a/\346\226\207\346\241\243/image/QQ20241223-211716.png" "b/\346\226\207\346\241\243/image/QQ20241223-211716.png"
new file mode 100644
index 0000000000000000000000000000000000000000..eba722272177ae50daaa6d76cc87b43164848d38
Binary files /dev/null and "b/\346\226\207\346\241\243/image/QQ20241223-211716.png" differ
diff --git "a/\346\226\207\346\241\243/image/QQ20241223-212050.png" "b/\346\226\207\346\241\243/image/QQ20241223-212050.png"
new file mode 100644
index 0000000000000000000000000000000000000000..d3c9a62db6da851321b49e677fae540859729765
Binary files /dev/null and "b/\346\226\207\346\241\243/image/QQ20241223-212050.png" differ
diff --git "a/\346\226\207\346\241\243/image/QQ20241223-212523.png" "b/\346\226\207\346\241\243/image/QQ20241223-212523.png"
new file mode 100644
index 0000000000000000000000000000000000000000..d6b34da4196f7c632f0d8ffbd4673104228a4340
Binary files /dev/null and "b/\346\226\207\346\241\243/image/QQ20241223-212523.png" differ
diff --git "a/\346\226\207\346\241\243/image/QQ20241223-213317.png" "b/\346\226\207\346\241\243/image/QQ20241223-213317.png"
new file mode 100644
index 0000000000000000000000000000000000000000..d914162e87c525add13ad365d14fadf1e466b76b
Binary files /dev/null and "b/\346\226\207\346\241\243/image/QQ20241223-213317.png" differ
diff --git "a/\346\226\207\346\241\243/image/QQ20241223-221654.png" "b/\346\226\207\346\241\243/image/QQ20241223-221654.png"
new file mode 100644
index 0000000000000000000000000000000000000000..953f2443fadeffcd64e38f92c46a9e28b2767438
Binary files /dev/null and "b/\346\226\207\346\241\243/image/QQ20241223-221654.png" differ
diff --git "a/\346\226\207\346\241\243/image/QQ20241223-222942.png" "b/\346\226\207\346\241\243/image/QQ20241223-222942.png"
new file mode 100644
index 0000000000000000000000000000000000000000..3d22f7185567b0ce09f55ea2dfab6a94ea070d48
Binary files /dev/null and "b/\346\226\207\346\241\243/image/QQ20241223-222942.png" differ
diff --git "a/\346\226\207\346\241\243/image/QQ20241223-223904.png" "b/\346\226\207\346\241\243/image/QQ20241223-223904.png"
new file mode 100644
index 0000000000000000000000000000000000000000..608c32596f2ede728a11f0a936449d2ad11e6be9
Binary files /dev/null and "b/\346\226\207\346\241\243/image/QQ20241223-223904.png" differ
diff --git "a/\346\226\207\346\241\243/image/QQ20241223-224648.png" "b/\346\226\207\346\241\243/image/QQ20241223-224648.png"
new file mode 100644
index 0000000000000000000000000000000000000000..22ca0c0708ac72f051f27b54afee21e58d17d481
Binary files /dev/null and "b/\346\226\207\346\241\243/image/QQ20241223-224648.png" differ
diff --git "a/\346\226\207\346\241\243/image/QQ20241223-225538.png" "b/\346\226\207\346\241\243/image/QQ20241223-225538.png"
new file mode 100644
index 0000000000000000000000000000000000000000..d7cb0d0b9860e06cb945ba898c054938c215b67d
Binary files /dev/null and "b/\346\226\207\346\241\243/image/QQ20241223-225538.png" differ
diff --git "a/\346\226\207\346\241\243/image/QQ20241223-230214.png" "b/\346\226\207\346\241\243/image/QQ20241223-230214.png"
new file mode 100644
index 0000000000000000000000000000000000000000..4c1e94f284a6f37551eb4c62430c71bede15e4dd
Binary files /dev/null and "b/\346\226\207\346\241\243/image/QQ20241223-230214.png" differ
diff --git "a/\346\226\207\346\241\243/image/QQ20241223-230416.png" "b/\346\226\207\346\241\243/image/QQ20241223-230416.png"
new file mode 100644
index 0000000000000000000000000000000000000000..b639b499fcbcbdc1285963a5fddb9226e8485460
Binary files /dev/null and "b/\346\226\207\346\241\243/image/QQ20241223-230416.png" differ
diff --git "a/\346\226\207\346\241\243/\345\214\272\345\237\237\350\265\233\346\226\207\346\241\243.md" "b/\346\226\207\346\241\243/\345\214\272\345\237\237\350\265\233\346\226\207\346\241\243.md"
new file mode 100644
index 0000000000000000000000000000000000000000..4c56db1e6cc32de746b5554f4d76bbec8bf8b8d2
--- /dev/null
+++ "b/\346\226\207\346\241\243/\345\214\272\345\237\237\350\265\233\346\226\207\346\241\243.md"
@@ -0,0 +1,1613 @@
+# 区域赛文档
+
+## **仓库准备**
+
+拉取仓库
+
+```bash
+git clone https://gitlab.eduxiji.net/2019301887/oskernel2022-npucore.git
+
+cd oskernel2022-npucore
+```
+
+清除git历史记录,删去 .git 隐藏文件夹
+
+linux系统下:
+
+```bash
+rm -rf .git 
+```
+
+## **比赛平台仓库建立**
+
+1.按照手册建立仓库。
+
+2.因为`riscv64-linux-musl-cross`在测评平台会提供,因此无需上传至仓库,剩下的文件可以一次提交。
+
+按照如下指令执行:
+
+```bash
+git config --global user.name "wyyhhh"
+git config --global user.email "t202410699994200@eduxiji.net"
+git init --initial-branch=main
+git remote add origin https://gitlab.eduxiji.net/T202410699994200/oskernel2024-wyh-NPU.git
+(可省略
+git add riscv64-linux-musl-cross
+git commit -m "Initial commit"
+git push -u origin main
+)
+git add .
+git commit -m "Initial commit"
+git push -u origin main
+```
+
+如果不小心把`riscv64-linux-musl-cross`提交至仓库,可以通过以下命令将仓库中的删除:
+
+```bash
+1.如果你只想从版本控制中删除文件夹而保留本地副本,可以加上 --cached:
+git rm -r --cached folder_name
+2.提交更改: 提交删除操作:
+git commit -m "Deleted folder riscv64-linux-musl-cross"
+3.推送到远程仓库: 推送更改到 GitLab:
+git push origin branch_name
+将 branch_name 替换为你正在使用的分支(如 main 或 master)。
+```
+
+以上内容进行完成之后,就可以在平台上编译运行成功,得到一个0分的结果了。具体怎么得到分数,需要下面的步骤。
+
+## 刷入测评镜像
+
+### 详情可参考如下链接中的步骤:
+
+[适配区域赛评测]: https://gitlab.eduxiji.net/202310699101073/oskernel2023-npucore-plus/-/blob/comp-1-qemu/docs/ForOJ.md
+
+### 除了文档中的步骤还需要:
+
+**1**.**完善shutdown()的系统调用**
+
+os中的调用:
+
+![](image/QQ20241223-204809.png)
+
+user中的调用:
+
+![](image/QQ20241223-204848.png)
+
+**2**.**将RELEASE变量设置为 --release,将构建优化为发布版本**
+
+![](image/QQ20241223-204951.png)
+
+以上内容进行完成之后,就可以得到一个初步的分数了。
+
+## 具体系统调用的完成
+
+如果使用的是2022版的NPUcore,到这里就可以得到91分,还剩下4个系统调用等待完善,分别是openat()、getppid()、pipe()、dup2()。
+
+小提示:想要修改出正确的系统调用,当然要知道平台是怎么测评自己的系统调用的,可以在下面的链接中查看测评的.c文件和.py文件:
+
+[测例代码]: https://github.com/oscomp/testsuits-for-oskernel/tree/main/riscv-syscalls-testing/user/src/oscomp
+
+想要在本地跑测评,就需要在下面的链接下载压缩包:
+
+[测评镜像压缩包]: https://gitlab.eduxiji.net/202310699101073/oskernel2023-npucore-plus/-/blob/comp-1-qemu/sdcard.img.gz
+
+将压缩包放在根目录下,然后来解压 sdcard.img.gz:
+
+```bash
+gunzip -c sdcard.img.gz > sdcard.img
+```
+
+运行命令(每次运行前要先`make clean`):
+
+```bash
+make all
+qemu-system-riscv64 -machine virt -kernel kernel-qemu -m 128M -nographic -smp 2 -bios sbi-qemu -drive file=sdcard.img,if=none,format=raw,id=x0  -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0
+```
+
+下面依次进行4个系统调用的完善:
+
+### 1.openat(打开文件)
+
+运行后发现光标卡住了:
+
+![](image/QQ20241223-210122.png)
+
+这时候就需要进行GDB调试,逐步观察代码在哪里停下,发现停在这一行:
+
+![](image/QQ20241223-210809.png)
+
+那么一定是这一行出现的问题,往前看,可以看到`681`行有一个和该行几乎一样的表达,而且由于这行代码的意思是给`task`加锁以防止多进程并发可能导致的问题,所以两次加锁就可以断定这里产生了`死锁`,和光标停顿的现象也吻合。
+
+将该行注释掉之后,再次运行就可以发现该测试点已经通过,分数也来到了95分。
+
+### 2.getppid(获取父进程 ID )
+
+首先要明白PPID是(Parent Process ID)的缩写,指的是父进程的进程ID。
+
+直接运行得到结果:
+
+![](image/QQ20241223-211716.png)
+
+这时候会一头雾水,怎么才能算success呢,这时测例代码就起了大作用:
+
+```c
+int test_getppid()
+{
+    TEST_START(__func__);
+    pid_t ppid = getppid();
+    if(ppid > 0) printf("  getppid success. ppid : %d\n", ppid);
+    else printf("  getppid error.\n");
+    TEST_END(__func__);
+}
+```
+
+很明显可以看出需要ppid大于0,才能通过测评,那么现在的ppid是多少呢?
+
+可以打印一下:
+
+![](image/QQ20241223-212050.png)
+
+ppid原来是0,那确实无法通过。如果仅仅想通过测评,可以简单地将函数返回值强制设置为1,而不关心ppid的真实值,但是这显然不是主办方想要的,就需要追寻更深层的原因,遇事不决GDB:
+
+在父进程创建的某一步,发现来到了这里:
+
+![](image/QQ20241223-212523.png)
+
+这一步的功能是在pid分配器中弹出一个空闲的pid给当前进程,那么ppid应该是在这一步得到0,观察一下,发现current此时是0,那弹出的可不就是0嘛,完全对上了,想要ppid大于0,就把分配器中的初始pid(也就是current)设置为从1开始不就好了吗,修改之后再次运行:
+
+![](image/QQ20241223-213317.png)
+
+完美通过!打印的ppid也确实是1了,但是别得意忘形了,一定要记得把打印语句去掉再提交,因为测评文件是严格匹配字符的,打印字符会扰乱,导致0分。这时候应该可以得到96分。
+
+### 3.pipe(创建管道)
+
+直接运行:
+
+![](image/QQ20241223-221654.png)
+
+上测例代码:
+
+```python
+def test(self, data):
+        self.assert_ge(len(data), 3)
+        cpid0 = False
+        cpid1 = False
+        for line in data[:3]:
+            if line == "cpid: 0":
+                cpid0 = True
+                continue
+            r = re.findall(r"cpid: (\d+)", line)
+            if r and int(r[0]) > 0:
+                cpid1 = True
+                continue
+        self.assert_equal(cpid0, True)
+        self.assert_equal(cpid1, True)
+        self.assert_equal(data[2], "  Write to pipe successfully.")
+```
+
+发现,并没有打印"  Write to pipe successfully."
+
+pipe.c:
+
+```c
+void test_pipe(void){
+    TEST_START(__func__);
+    int cpid;
+    char buf[128] = {0};
+    int ret = pipe(fd);
+    assert(ret != -1);
+    const char *data = "  Write to pipe successfully.\n";
+    cpid = fork();
+    printf("cpid: %d\n", cpid);
+    if(cpid > 0){
+        close(fd[1]);
+        while(read(fd[0], buf, 1) > 0)
+            write(STDOUT, buf, 1);
+        write(STDOUT, "\n", 1);
+        close(fd[0]);
+        wait(NULL);
+    }else{
+        close(fd[0]);
+        write(fd[1], data, strlen(data));
+        close(fd[1]);
+        exit(0);
+    }
+    TEST_END(__func__);
+}
+```
+
+cpid>0时读,cpid=0时写,于是父进程为读端,子进程为写端,代码涉及的系统调用有两个,pipe()和read(),因为调试并未发现pipe()有错误,那么就把目光放在read()上,还是一样的,看一下在哪里出的测试点:
+
+![](image/QQ20241223-222942.png)
+
+在这一步之后read_user函数就准备退出了,所以这个if判断是成立的,在变量处也可以看到sigmask=0,sigpending=65536(2^16),而这个值就是clone函数中的一个参数,即SIGCHLD,代表子进程的退出,子进程的退出代表管道的写端已经完成了写,下面要到父进程(即读端)进行读取,但是由于父进程收到子进程退出的信号SIGCHLD,导致if判断成立,还没有进行读取便结束读端,优先处理子进程的退出,到这里改法也逐渐明朗,测试点想要的无非是父进程可以读到字符,然后把字符显示在终端,那把整个if判断去掉,使父进程读完再退出就可以,再次运行:
+
+![](image/QQ20241223-223904.png)
+
+GDB跟踪也是可以看出父进程每次读一个字符,然后打印。这次就通过了,分数也来到了100分。
+
+### 4.dup2(文件描述符复制并设置标志)
+
+根据系统调用号要明白dup2就是代码中的dup3。
+
+直接运行:
+
+![](image/QQ20241223-224648.png)
+
+dup2.c:
+
+```c
+void test_dup2(){
+	TEST_START(__func__);
+	int fd = dup2(STDOUT, 100);
+	assert(fd != -1);
+	const char *str = "  from fd 100\n";
+	write(100, str, strlen(str));
+	TEST_END(__func__);
+}
+```
+
+发现应该打印出"  from fd 100\n",也是有点无从下手,直接开始调试:
+
+首先到这一步:
+
+![](image/QQ20241223-225538.png)
+
+可以看到变量部分,current=4,pos=100,所以要到else部分,首先判断pos是否大于soft_limit,也就是软件要求的最大值,再下一步这个函数就返回了,于是推断pos大于soft_limit,直接返回Error了,查找soft_limit的值:
+
+![](image/QQ20241223-230214.png)
+
+发现soft_limit=64,确实小于pos的100,那改大不就好了,为了保证计算机内部二进制,改为128即可,再次运行:
+
+![](image/QQ20241223-230416.png)
+
+成功打印出结果,分数也来到了102分,至此,系统调用的修改告一段落。
+
+## 操作系统整体讲解
+
+### 系统调用
+
+#### 概述
+
+1. **系统调用的概念与作用**
+   - **系统调用的定义**:系统调用是应用程序与操作系统内核之间的接口,它允许应用程序请求内核执行特定的操作,如文件读写、进程管理、设备控制等。在 NPUcore 操作系统中,系统调用是通过软件中断(如 ecall 指令)触发的,将控制权从用户态转移到内核态,由内核执行相应的操作。
+   - **系统调用的作用**:操作系统通过系统调用来提供对硬件资源的抽象和管理,使得应用程序能够方便地使用系统资源,而无需了解硬件的细节。同时,系统调用也提供了一种安全机制,防止应用程序直接访问内核数据和执行敏感操作,保护系统的稳定性和安全性。
+2. **系统调用的实现机制**
+   - **中断机制**:在 RISC-V 架构下,系统调用是通过 ecall 指令触发的中断来实现的。当用户态程序执行 ecall 指令时,会陷入内核态,跳转到 STVEC 寄存器中指定的中断处理程序(trap_handler 函数)进行相应的操作。中断处理程序会根据系统调用号来分发处理不同的系统调用。
+   - **寄存器操作**:在执行系统调用时,需要将参数传递给内核。NPUcore 通过特定的寄存器约定来传递参数,例如使用 a0-a7 寄存器传递系统调用号和参数。同时,内核也会使用寄存器来保存系统调用的返回值,以便返回给用户态程序。
+   - **函数调用**:在内核态,系统调用会通过函数调用来实现具体的功能。例如,在 NPUcore 中,write 系统调用会调用内核中的相关函数来执行文件写入操作,这些函数会根据系统调用的参数和内核的状态来完成具体的工作。
+3. **系统调用的具体流程**
+   - **用户态进程调用 syscall**:应用程序通过调用用户态库函数(如 write 函数),库函数会进一步调用 syscall 函数,将系统调用号和参数传递给内核。例如,在 user/src/usr_call.rs 中的 write 函数会调用 sys_write 函数,sys_write 函数再调用 syscall 函数。
+   - **syscall 调用 ecall 指令陷入内核态**:syscall 函数中的内联汇编代码执行 ecall 指令,触发中断,使程序陷入内核态。此时,硬件会自动保存用户态的上下文,并跳转到内核态的中断处理程序。
+   - **内核态处理系统调用**:在内核态,中断处理程序(trap_handler 函数)会根据系统调用号来分发处理不同的系统调用。例如,对于 write 系统调用,会调用内核中的相应函数来执行文件写入操作,并将结果返回给用户态。
+   - **恢复用户态上下文并返回**:系统调用处理完成后,内核会恢复用户态的上下文,并通过 sret 指令返回用户态,继续执行用户态程序的后续指令。
+4. **常见系统调用示例**
+   - **fork 系统调用**:用于创建一个新的进程,新进程是原进程的副本,包括代码、数据和堆栈等资源。在 NPUcore 中,fork 系统调用通过调用 clone 函数来实现,clone 函数可以更精确地控制进程之间的执行上下文细节,如共享内存空间、文件描述符表等。
+   - **exec 系统调用**:用于加载并执行一个新的程序,替换当前进程的地址空间和上下文环境。在 NPUcore 中,exec 系统调用会打开指定的可执行文件,检查文件格式,将文件内容加载到内存中,并设置新的进程上下文,然后开始执行新程序。
+   - **sbrk 系统调用**:用于动态调整进程的堆空间大小。在 NPUcore 中,sbrk 系统调用通过 Memoryset 接口下的 sbrk 函数实现,它可以根据传入的参数增加或缩小堆空间,并返回新的堆顶指针。同时,sbrk 函数会调用 mmap 或 munmap 函数来实现堆空间的映射和释放。
+5. **系统调用与其他操作系统概念的关系**
+   - **与进程管理的关系**:系统调用在进程管理中起着重要作用,如 fork、exec 等系统调用用于创建和执行进程,而 getpid 系统调用用于获取当前进程的 ID。进程的调度和切换也与系统调用密切相关,例如,当一个进程执行系统调用时,可能会导致进程阻塞或被调度到其他进程执行。
+   - **与内存管理的关系**:系统调用与内存管理相互关联,例如,sbrk 系统调用用于动态分配内存,而 mmap 系统调用用于将文件映射到内存空间。内存管理模块负责为系统调用分配和管理内存资源,确保系统调用的正常执行。
+   - **与文件系统的关系**:文件系统相关的系统调用(如 open、read、write 等)用于实现对文件的操作。这些系统调用与文件系统的接口层、缓存层和索引节点层等密切配合,完成文件的打开、读取、写入和关闭等操作,实现数据的持久化存储。
+
+#### 具体实现机制
+
+1. **系统调用的触发与陷入内核态**
+   - **用户态进程调用 syscall**:应用程序通过调用用户态库函数发起系统调用。以 write 系统调用为例,在 user/src/usr_call.rs 中,write 函数被应用程序调用,它进一步调用 sys_write 函数,如`pub fn write(fd: usize, buf: &[u8]) -> isize { sys_write(fd, buf) }`。sys_write 函数则调用 syscall 函数,并传递系统调用号和参数,如`pub fn sys_write(fd: usize, buffer: &[u8]) -> isize { syscall(SYSCALL_WRITE, [fd, buffer.as_ptr() as usize, buffer.len()]) }`。
+   - **syscall 调用 ecall 指令陷入内核态**:在 syscall 函数中,通过内联汇编执行 ecall 指令触发中断,使程序陷入内核态。具体代码如下:
+
+```rust
+fn syscall(id: usize, args: [usize; 3]) -> isize {
+    let mut ret: isize;
+    unsafe {
+        asm!(
+            "ecall",
+            inlateout("x10") args[0] => ret,
+            in("x11") args[1],
+            in("x12") args[2],
+            in("x17") id
+        );
+    }
+    ret
+}
+```
+
+在这段代码中,ecall 指令触发中断,将系统调用号和参数传递给内核。inlateout 指令将参数 args [0] 放入 a0 寄存器,并在函数返回后将 x10 寄存器的值作为返回值赋给 ret。
+
+2. **内核态对系统调用的处理**
+
+- **设置 stvec 寄存器与 trap 处理**:在操作系统初始化时,通过`set_user_trap_entry`函数设置 stvec 寄存器,使其指向正确的 Trap 处理入口点(`TRAMPOLINE`)。当用户态进程执行 ecall 指令陷入内核态后,CPU 会跳转到 stvec 寄存器指向的地址,开始执行 Trap 处理程序。在 trap 处理程序中,首先通过`__alltraps`将 Trap 上下文保存在内核栈上,然后跳转到使用 Rust 编写的 trap_handler 函数完成 Trap 分发及处理。
+- **trap_handler 函数中的系统调用分发**:trap_handler 函数根据系统调用号对操作进行分发处理。以处理系统调用的部分代码为例:
+
+```rust
+pub fn trap_handler() ->! {
+    set_kernel_trap_entry();
+    let scause = scause::read();
+    match scause.cause() {
+        Trap::Exception(Exception::UserEnvCall) => {
+            let mut cx = current_trap_cx();
+            cx.gp.pc += 4;
+            let result = syscall(
+                cx.gp.a7,
+                [cx.gp.a0, cx.gp.a1, cx.gp.a2, cx.gp.a3, cx.gp.a4, cx.gp.a5],
+            );
+            cx = current_trap_cx();
+            cx.gp.a0 = result as usize;
+        }
+        //...
+    trap_return();
+}
+```
+
+在这段代码中,首先读取 scause 寄存器获取 Trap 原因,当判断为用户环境调用(UserEnvCall)时,获取当前 Trap 上下文,将程序计数器(pc)加 4 以指向下一条指令,然后调用内核态的 syscall 函数进行具体的系统调用处理,并将结果保存到返回值寄存器 a0 中。
+
+3. **系统调用的具体功能实现(以 write 系统调用为例)**
+
+- **内核态 write 系统调用的处理流程**:在内核态,write 系统调用最终会调用到 os/src/syscall/mod.rs 中的 syscall 函数,根据系统调用号进行分发处理。对于 write 系统调用(SYSCALL_WRITE),会调用相应的功能函数(如 sys_write 等)来执行具体的文件写入操作。
+- **涉及的相关函数与数据结构**:在 write 系统调用的实现中,涉及到多个函数和数据结构的协同工作。例如,在用户态传递的文件描述符(fd)和缓冲区指针(buf)等参数,在内核态会通过文件描述符表(FdTable)找到对应的文件描述符对象,然后调用文件对象的 write_user 方法将数据写入文件。文件描述符表用于管理进程打开的文件,每个文件描述符对应一个文件对象,文件对象实现了 File Trait,定义了一系列文件操作的接口函数,如 write、read 等。
+
+   4.**系统调用与其他模块的交互**(**如内存管理**、**进程管理**)
+
+- **与内存管理的交互**:在一些系统调用中,如 sbrk 系统调用用于动态调整进程的堆空间大小,它与内存管理模块密切相关。sbrk 系统调用会调用 Memoryset 接口下的 sbrk 函数,该函数可能会调用 mmap 或 munmap 函数来实现堆空间的映射和释放,涉及到内存管理中的页表操作、物理内存分配等功能。
+- **与进程管理的交互**:系统调用在进程管理中也发挥着重要作用。例如,fork 系统调用用于创建新进程,它涉及到进程控制块(TCB)的创建、进程标识符(PID)的分配、内存空间的复制等操作,与进程管理模块中的相关数据结构和函数紧密协作。同时,进程的调度和切换也可能由系统调用触发,如 sys_yield 系统调用用于让出当前进程的占用权,涉及到进程状态的改变、就绪队列的管理等进程管理相关的操作。
+
+### 内存管理
+
+#### 1. 内存布局与虚拟地址空间
+
+##### 1.1 内核虚拟地址空间分布
+
+在NPUcore中,内核虚拟地址空间被精心规划以确保各个组件之间不会相互干扰,并且提供了足够的灵活性来满足不同应用场景的需求。以下是几个关键部分:
+
+- **Trampoline**:位于最高虚拟页面上,主要用于处理panic等异常情况。
+- **User Stack**:紧随其后的是用户栈区域,每个用户栈占用一页大小(4KB),并且向下增长。
+- **Guard Page**:为了防止栈溢出导致的安全问题,在相邻两个内核栈之间预留了一个保护页面(同样为一页大小)。
+- **Kernel Program**:再往下则是加载内核程序的地方。
+
+此外,还有其他一些辅助性的区域,如用于存放全局变量或静态数据的段落等。这种层次化的结构使得系统能够更加有序地管理和利用有限的内存资源。
+
+##### 1.2 用户进程的虚拟地址空间
+
+对于每一个用户进程来说,它都有自己独立的一块虚拟地址空间。这块空间由操作系统负责创建并维护,保证了进程间的隔离性。当新进程启动时,它的初始地址空间为空;随着进程运行过程中不断申请新的页面,所占的物理内存也会逐渐增加。为了实现这一点,操作系统需要提供一套完整的API供用户进程调用来请求和释放内存。
+
+#### 2. 虚拟地址到物理地址的转换
+
+##### 2.1 地址格式
+
+NPUcore采用页式管理方案,其中每个页面大小固定为4KiB (2^12 B)。这意味着虚拟地址可以分为两部分:高27位作为虚拟页号(VPN),低12位作为页内偏移(Page Offset)。同样地,物理地址也可以拆分成高44位的物理页号(PPN)加上相同的12位页内偏移。这样的设计简化了地址转换的过程,同时也便于硬件实现。
+
+##### 2.2 页表映射
+
+为了完成从虚拟地址到物理地址的转换,NPUcore引入了多级页表结构。每当有新的页面被分配给某个进程时,相应的页表项就会被更新,从而建立起虚拟地址与物理地址之间的对应关系。具体来说,这个过程涉及以下几个步骤:
+
+1. 检查传入的虚拟页号(VPN)是否已经被分配过;
+2. 如果未分配,则根据所需类型选择合适的映射方式(例如恒等映射或者普通映射);
+3. 对于普通映射,调用`frame_alloc_uninit()`函数来获取一个空闲的物理页面;
+4. 更新页表,建立VPN到PPN的映射;
+5. 返回最终得到的物理地址。
+
+通过这种方式,即使应用程序只需要少量连续的虚拟地址,它们也可能分散在整个物理内存的不同位置,但对应用本身而言却是透明的。
+
+#### 3. 物理内存分配器
+
+##### 3.1 分配器初始化
+
+在编写任何应用程序之前,必须先对内存进行正确的初始化操作,否则可能会引发各种错误,比如内存泄漏甚至程序崩溃。因此,在NPUcore启动阶段,会执行一系列初始化工作,其中包括设置好物理内存分配器的工作范围。这部分代码在`frame_allocator.rs`文件中:
+
+```rust
+pub fn init_frame_allocator(){
+    extern "C" {
+        fn ekernel();
+    }
+    FRAME_ALLOCATOR.write().init(
+        PhysAddr::from(ekernel as usize).ceil(),
+        PhysAddr::from(MEMORY_END).floor(),
+    );
+}
+```
+
+这里`ekernel`指代内核空间结束的位置,而`MEMORY_END`则定义了整个可用物理内存的最大边界。两者之间的所有地址都将被分配器管理起来,供后续使用。
+
+##### 3.2 分配器接口
+
+NPUcore提供了一套简洁易用的API。这些API隐藏了许多底层细节,让用户无需关心具体的实现逻辑。以下是几个核心方法:
+
+- `new()`:创建一个新的分配器实例。
+- `alloc()`:分配一个物理页面,并返回一个包含该页面信息的对象(`FrameTracker`)。
+- `dealloc(ppn: PhysPageNum)`:回收指定物理页号对应的页面。
+
+值得注意的是,还有一个名为`alloc_uninit()`的方法,它可以跳过初始化步骤直接返回页面对象,适用于那些不需要额外准备工作的场景,这样可以加快分配速度。
+
+##### 3.3 栈式管理策略
+
+为了进一步优化性能,NPUcore采用了基于栈的数据结构来组织空闲页面池。每当有新的页面需求时,分配器会从栈顶弹出最上面的一个页面;相反地,当页面不再需要时,则会被压回到栈底。这种方法保证了每次分配都能尽可能快地完成,同时减少了碎片化带来的负面影响。
+
+#### 4. 页面置换算法
+
+虽然我们尽量避免频繁地换入换出页面,但在某些情况下仍然不可避免地会发生这种情况。为此,NPUcore实现了一套页面置换算法,用以决定哪些页面应该被替换出去以腾出空间。这通常是基于某种启发式的规则来进行判断,例如最近最少使用(LRU)或随机选择(RAND)等策略。
+
+### 进程管理
+
+#### 重要数据结构
+
+1. **进程标识符**`PidHandle`
+   - 定义:`pub struct PidHandle(pub usize);`,是一个 64 位无符号整数,用于唯一标识进程 ID。
+   - 分配与回收:由标识符分配器`RecycleAllocator`管理,`RecycleAllocator`通过`current`字段记录下一个分配的`pid`进程号 / 内核栈号(若`recycled`数组为空),每次分配后`current`加 1;`recycled`数组保存已回收的`pid`号。其提供了`alloc`分配器方法(若`recycled`中有可用`pid`号则直接分配,否则生成新`pid`号)、`dealloc`回收器方法(检查合法性后回收`pid`号)和`get_allocated`方法(获取当前正在运行的进程数量)。
+   - 示例代码:
+
+```rust
+lazy_static! {
+    static ref PID_ALLOCATOR: Mutex<RecycleAllocator> = Mutex::new(RecycleAllocator::new());
+}
+
+pub fn pid_alloc() -> PidHandle {
+    PidHandle(PID_ALLOCATOR.lock().alloc())
+}
+
+impl Drop for PidHandle {
+    fn drop(&mut self) {
+        PID_ALLOCATOR.lock().dealloc(self.0);
+    }
+}
+```
+
+2. **内核栈**`KernelStack`
+
+- 存在意义:系统调用陷入内核后,用于支持函数调用和自动变量。每个进程都有独自的内核栈,避免进程间共享内核栈导致数据破坏。
+- 方法:
+  - `kernel_stack_position`:返回当前内核栈在内核空间中的栈顶和栈底位置。
+  - `kstack_alloc`:内核栈分配器,从`KSTACK_ALLOCATOR`获取内核栈编号,计算栈位置后插入内核地址空间。
+  - `drop`:将内核栈从内核空间移除,释放相关资源。
+  - `push_on_top`:将变量压入内核栈顶并返回裸指针。
+- 示例代码:
+
+```rust
+pub fn kernel_stack_position(kstack_id: usize) -> (usize, usize) {
+    let top = TRAMPOLINE - kstack_id * (KERNEL_STACK_SIZE + PAGE_SIZE);
+    let bottom = top - KERNEL_STACK_SIZE;
+    (bottom, top)
+}
+
+pub fn kstack_alloc() -> KernelStack {
+    let kstack_id = KSTACK_ALLOCATOR.lock().alloc();
+    let (kstack_bottom, kstack_top) = kernel_stack_position(kstack_id);
+    KERNEL_SPACE.lock().insert_framed_area(
+        kstack_bottom.into(),
+        kstack_top.into(),
+        MapPermission::R | MapPermission::W,
+    );
+    KernelStack(kstack_id)
+}
+
+impl Drop for KernelStack {
+    fn drop(&mut self) {
+        let (kernel_stack_bottom, _) = kernel_stack_position(self.0);
+        let kernel_stack_bottom_va: VirtAddr = kernel_stack_bottom.into();
+        KERNEL_SPACE
+           .lock()
+           .remove_area_with_start_vpn(kernel_stack_bottom_va.into())
+           .unwrap();
+        KSTACK_ALLOCATOR.lock().dealloc(self.0)
+    }
+}
+
+impl KernelStack {
+    #[allow(unused)]
+    pub fn push_on_top<T>(&self, value: T) -> *mut T
+    where
+        T: Sized,
+    {
+        let kernel_stack_top = self.get_top();
+        let ptr_mut = (kernel_stack_top - core::mem::size_of::<T>()) as *mut T;
+        unsafe {
+            *ptr_mut = value;
+        }
+        ptr_mut
+    }
+
+    pub fn get_top(&self) -> usize {
+        let (_, kernel_stack_top) = kernel_stack_position(self.0);
+        kernel_stack_top
+    }
+}
+```
+
+   3.**进程控制块**`TCB(Task Control Block)`
+
+- 作用:记录操作系统管理进程所需的全部信息,是进程存在的唯一标志,实现进程间断性运行、提供管理和调度所需信息,以及进程间的同步和通信。
+- 组成:由可变部分、不可变部分及可共享且可变部分组成。
+  - 不可变部分包括`pid`(进程号)、`kstack`(内核栈)、`ustack_base`(用户栈基地址)、`exit_signal`(退出信号)。
+  - 可变部分为`inner`,其类型`TaskControlBlockInner`包含`sigmask`、`sigpending`、`trap_cx_ppn`(存放`Trap`上下文的物理地址)、`task_cx`(进程上下文)、`task_status`(进程状态,有`Ready`、`Running`、`Zombie`、`Interruptible`四种状态)、`parent`(父进程弱引用)、`children`(子进程列表)、`exit_code`(退出码)、`clear_child_tid`等。
+- 示例代码:
+
+```rust
+pub struct TaskControlBlock {
+    // immutable
+    pub pid: PidHandle,
+    pub tid: usize,
+    pub tgid: usize,
+    pub kstack: KernelStack,
+    pub ustack_base: usize,
+    pub tui: Signals,
+    // mutable
+    inner: Mutex<TaskControlBlockInner>,
+    // shareable and mutable
+    pub exe: Arc<Mutex<FileDescriptor>>,
+    pub tid_allocator: Arc<Mutex<RecycleAllocator>>,
+    pub files: Arc<Mutex<FdTable>>,
+    pub fs: Arc<Mutex<FsStatus>>,
+    pub vm: Arc<Mutex<MemorySet>>,
+    pub sighand: Arc<Mutex<Vec<Option<Box<SigAction>>>>>,
+    pub futex: Arc<Mutex<Futex>>,
+}
+
+pub struct TaskControlBlockInner {
+    pub sigmask: Signals,
+    pub sigpending: Signals,
+    pub trap_cx_ppn: PhysPageNum,
+    pub task_cx: TaskContext,
+    pub task_status: TaskStatus,
+    pub parent: Option<Weak<TaskControlBlock>>,
+    pub children: Vec<Arc<TaskControlBlock>>,
+    pub exit_code: u32,
+    pub clear_child_tid: usize,
+    pub robust_list: RobustList,
+    pub heap_bottom: usize,
+    pub heap_pt: usize,
+    pub pgid: usize,
+    pub rusage: Rusage,
+    pub clock: ProcClock,
+    pub timer: [ITimerVal; 3],
+}
+```
+
+#### 进程创建
+
+1. **总体流程**
+
+   - 从系统文件中找到内核加载第一个初始进程的`elf`文件,获取其数据和内容,调用`TCB`中的`new()`方法创建内核的第一个进程`initproc`。
+   - 其余进程由`initproc`通过`clone`系统调用克隆而来,然后新进程通过`exec`系统调用加载独立的程序代码和数据文件。
+
+2. `initproc`**的创建**
+
+   - 初始化`INITPROC`:通过`lazy_static!`宏懒分配`INITPROC`,调用`ROOT_FD.open`获取`elf`文件数据,再调用`TaskControlBlock::new(elf)`创建进程控制块。
+
+   - ```
+     TaskControlBlock::new
+     ```
+
+     方法:
+
+     - 获取`elf`可执行文件数据。
+     - 从`ELF`文件解析应用地址空间、用户堆顶位置和`elf`信息,移除地址映射关系。
+     - 初始化线程分配器,分配`pid`、`tid`、`tgid`和内核栈,记录内核栈位置。
+     - 查找`Trap`上下文物理页帧,初始化进程控制块各个字段,包括`exe`(可执行文件)、`tid_allocator`(线程分配器)、`files`(文件描述符表)、`fs`(文件系统状态)、`vm`(内存集合)、`sighand`(信号处理)、`futex`(互斥锁)等,设置进程状态为`Ready`。
+     - 初始化`Trap`上下文,使进程能正确进入用户态和内核态。
+
+   - 示例代码:
+
+```rust
+lazy_static! {
+    pub static ref INITPROC: Arc<TaskControlBlock> = Arc::new({
+        let elf = ROOT_FD.open("initproc", OpenFlags::O_RDONLY, true).unwrap();
+        TaskControlBlock::new(elf)
+    });
+}
+
+pub fn new(elf: FileDescriptor) -> Self {
+    let elf_data = elf.map_to_kernel_space(MMAP_BASE);
+    let (mut memory_set, user_heap, elf_info) = MemorySet::from_elf(elf_data).unwrap();
+    crate::mm::KERNEL_SPACE
+       .lock()
+       .remove_area_with_start_vpn(VirtAddr::from(MMAP_BASE).floor())
+       .unwrap();
+    let tid_allocator = Arc::new(Mutex::new(RecycleAllocator::new()));
+    // alloc a pid and a kernel stack in kernel space
+    let pid_handle = pid_alloc();
+    let tid = tid_allocator.lock().alloc();
+    let tgid = pid_handle.0;
+    let pgid = pid_handle.0;
+    let kstack = kstack_alloc();
+    let kstack_top = kstack.get_top();
+    let trap_cx_ppn = memory_set
+       .translate(VirtAddr::from(trap_cx_bottom_from_tid(tid)).into())
+       .unwrap()
+       .ppn();
+    let task_control_block = Self {
+        pid: pid_handle,
+        tid,
+        tgid,
+        kstack,
+        ustack_base: ustack_bottom_from_tid(tid),
+        exit_signal: Signals::empty(),
+        exe: Arc::new(Mutex::new(elf)),
+        tid_allocator,
+        files: Arc::new(Mutex::new(FdTable::new({
+            let mut vec = Vec::with_capacity(144);
+            let tty = Some(ROOT_FD.open("/dev/tty", OpenFlags::O_RDWR, false).unwrap());
+            vec.resize(3, tty);
+            vec
+        }))),
+        fs: Arc::new(Mutex::new(FsStatus {
+            working_inode: Arc::new(
+                ROOT_FD
+                   .open(".", OpenFlags::O_RDONLY | OpenFlags::O_DIRECTORY, true)
+                   .unwrap(),
+            ),
+        })),
+        vm: Arc::new(Mutex::new(memory_set)),
+        sighand: Arc::new(Mutex::new({
+            let mut vec = Vec::with_capacity(64);
+            vec.resize(64, None);
+            vec
+        })),
+        futex: Arc::new(Mutex::new(Futex::new())),
+        inner: Mutex::new(TaskControlBlockInner {
+            sigmask: Signals::empty(),
+            sigpending: Signals::empty(),
+            trap_cx_ppn,
+            task_cx: TaskContext::goto_trap_return(kstack_top),
+            task_status: TaskStatus::Ready,
+            parent: None,
+            children: Vec::new(),
+            exit_code: 0,
+            clear_child_tid: 0,
+            robust_list: RobustList::default(),
+            heap_bottom: user_heap,
+            heap_pt: user_heap,
+            pgid,
+            rusage: Rusage::new(),
+            clock: ProcClock::new(),
+            timer: [ITimerVal::new(); 3],
+        }),
+    };
+    let trap_cx = task_control_block.acquire_inner_lock().get_trap_cx();
+    *trap_cx = TrapContext::app_init_context(
+        elf_info.entry,
+        ustack_bottom_from_tid(tid),
+        KERNEL_SPACE.lock().token(),
+        kstack_top,
+        trap_handler as usize,
+    );
+    task_control_block
+}
+```
+
+   3.`clone`**系统调用**
+
+- 作用:创建新进程或线程,精确控制调用进程和子进程之间的执行上下文细节,如共享虚拟地址空间、文件描述符表、信号句柄表等,还可将子进程放入不同命名空间。
+- 实现:
+  - 系统调用参数:`sys_clone`函数接受`flags`(`CloneFlags`类型,包含多种标志控制`clone`行为)、`stack`(子进程使用的栈位置)、`ptid`(与`CloneFlags`中的`CLONE_CHILD_SETTID`有关,保存线程 ID)、`tls`(与`CloneFlags`中的`CLONE_SETTLS`有关,保存`TLS`)、`ctid`(与`CloneFlags`中的`CLONE_PARENT_SETTID`有关,保存线程 ID)等参数。
+  - 函数体逻辑:
+    - 获取`exit_signal`,根据`flags`获取`clone flag`。
+    - 调用`parent.sys_clone`实现`clone`,在其中根据`flags`决定是否共享内存空间和线程组关系,分配`pid`、`tid`、`tgid`和内核栈,构建新进程`TCB`,设置`TCB`的各种属性(如共享资源标志决定是否共享文件描述符、文件系统、信号处理等),修改父进程的子进程表,设置新进程运行上下文和线程本地存储,最后返回新进程`TCB`。
+    - 根据`flags`判断是否设置`CLONE_PARENT_SETTID`、`CLONE_CHILD_SETTID`、`CLONE_CHILD_CLEARTID`等标志,执行相应操作(如将子进程线程 ID 写入指定位置、清除指定内存等)。
+    - 将新进程加入任务管理器的就绪队列。
+- 示例代码:
+
+```rust
+pub fn sys_clone(
+    flags: u32,
+    stack: *const u8,
+    ptid: *mut u32,
+    tls: usize,
+    ctid: *mut u32,
+) -> isize {
+    let exit_signal = match Signals::from_signum((flags & 0xff) as usize) {
+        Ok(signal) => signal,
+        Err(_) => {
+            warn!(
+                "[sys_clone] signum of exit_signal is unspecified or invalid: {}",
+                (flags & 0xff) as usize
+            );
+            // This is permitted by standard, but we only support 64 signals
+            Signals::empty()
+        }
+    };
+    // Sure to succeed, because all bits are valid (See `CloneFlags`)
+    let flags = CloneFlags::from_bits(flags &!0xff).unwrap();
+    show_frame_consumption! {
+        "clone";
+        let child = parent.sys_clone(flags, stack, tls, exit_signal);
+    }
+    if flags.contains(CloneFlags::CLONE_PARENT_SETTID) {
+        match translated_refmut(parent.get_user_token(), ptid) {
+            Ok(word) => *word = child.pid.0 as u32,
+            Err(errno) => return errno,
+        };
+    }
+    if flags.contains(CloneFlags::CLONE_CHILD_SETTID) {
+        match translated_refmut(child.get_user_token(), ctid) {
+            Ok(word) => *word = child.pid.0 as u32,
+            Err(errno) => return errno,
+        };
+    }
+    if flags.contains(CloneFlags::CLONE_CHILD_CLEARTID) {
+        child.acquire_inner_lock().clear_child_tid = ctid as usize;
+    }
+    add_task(child);
+    SUCCESS
+}
+
+pub fn sys_clone(
+    self: &Arc<TaskControlBlock>,
+    flags: CloneFlags,
+    stack: *const u8,
+    tls: usize,
+    exit_signal: Signals,
+) -> Arc<TaskControlBlock> {
+    // ---- hold parent PCB lock
+    let mut parent_inner = self.acquire_inner_lock();
+    // copy user space(include trap context)
+    let memory_set = if flags.contains(CloneFlags::CLONE_VM) {
+        self.vm.clone()
+    } else {
+        crate::mm::frame_reserve(16);
+        Arc::new(Mutex::new(MemorySet::from_existing_user(
+            &mut self.vm.lock(),
+        )))
+    };
+    let tid_allocator = if flags.contains(CloneFlags::CLONE_THREAD) {
+        self.tid_allocator.clone()
+    } else {
+        Arc::new(Mutex::new(RecycleAllocator::new()))
+    };
+    // alloc a pid and a kernel stack in kernel space
+    let pid_handle = pid_alloc();
+    let tid = tid_allocator.lock().alloc();
+    let tgid = if flags.contains(CloneFlags::CLONE_THREAD) {
+        self.tgid
+    } else {
+        pid_handle.0
+    };
+    let kstack = kstack_alloc();
+    let kstack_top = kstack.get_top();
+    if flags.contains(CloneFlags::CLONE_THREAD) {
+        memory_set.lock().alloc_user_res(tid, stack.is_null());
+    }
+    let task_control_block = Arc::new(TaskControlBlock {
+        pid: pid_handle,
+```
+
+#### 进程切换
+
+1. **实现方法**
+   - 依靠`os/src/task/mod.rs`中的`suspend_current_and_run_next`函数实现。该函数有两个应用场景:一是应用程序手动使用`sys_yield`系统调用让出当前进程占用权;二是内核出现错误造成`trap`时,程序会跳转至`os/src/trap/mod.rs`内部的中断处理程序,在其中调用`suspend_current_and_run_next`。
+2. **函数实现原理**
+   - 获取当前正在执行进程的`PCB`(命名为`task`),获取其可变部分(包含进程上下文信息)并转化为可变裸指针。
+   - 将当前进程状态改为`Ready`,手动减少引用计数(`drop`操作)。
+   - 将当前任务放入就绪队列(通过`add_task`函数),该函数将任务加入到懒分配的全局变量`TASK_MANAGER`的就绪队列中,`TASK_MANAGER`是`TaskManager`结构体的实例,包含就绪队列和可中断睡眠状态队列,用于管理进程调度。
+   - 调用`schedule`函数完成进程切换,`schedule`函数负责将当前进程切换到处理器调度进程(`idle task`),通过获取`PROCESSOR`(`Processor`结构体的懒分配全局变量)中的`idle_task_cx_ptr`(`idle`控制流的任务上下文指针),并使用汇编代码`__switch`实现上下文切换,将旧进程上下文保存到内存,从内存取出新进程上下文到 CPU 寄存器中。
+   - 示例代码:
+
+```rust
+pub fn suspend_current_and_run_next() {
+    // There must be an application running.
+    let task = take_current_task().unwrap();
+    // ---- hold current PCB lock
+    let mut task_inner = task.acquire_inner_lock();
+    let task_cx_ptr = &mut task_inner.task_cx as *mut TaskContext;
+    // Change status to Ready
+    task_inner.task_status = TaskStatus::Ready;
+    drop(task_inner);
+    // ---- release current PCB lock
+    // push back to ready queue.
+    add_task(task);
+    // jump to scheduling cycle
+    schedule(task_cx_ptr);
+}
+
+lazy_static! {
+    pub static ref TASK_MANAGER: Mutex<TaskManager> = Mutex::new(TaskManager::new());
+}
+
+pub fn add_task(task: Arc<TaskControlBlock>) {
+    TASK_MANAGER.lock().add(task);
+}
+
+impl TaskManager {
+    pub fn add(&mut self, task: Arc<TaskControlBlock>) {
+        self.ready_queue.push_back(task);
+    }
+}
+
+lazy_static! {
+    pub static ref PROCESSOR: Mutex<Processor> = Mutex::new(Processor::new());
+}
+
+pub fn schedule(switched_task_cx_ptr: *mut TaskContext) {
+    let idle_task_cx_ptr = PROCESSOR.lock().get_idle_task_cx_ptr();
+    unsafe {
+        __switch(switched_task_cx_ptr, idle_task_cx_ptr);
+    }
+}
+```
+
+#### 进程调度
+
+1. **调度策略**
+   - NPUcore 采用时间片轮转(RR)调度策略,将所有就绪线程按 FCFS 原则排成就绪队列,每次调度时将 CPU 分派给队首进程,执行一个时间片。时钟中断时,若进程时间片用完,将其送到就绪队列队尾,切换执行队首进程;若未用完,进程继续使用 CPU。
+   - 此外,NPUcore 支持阻塞式的进程调度模式,当进程因 IO 操作等无法获取资源时,会进入睡眠状态,让出 CPU,进入阻塞队列,直到被唤醒。唤醒进程的代码通常在中断处理程序中,因为硬件资源获得时往往伴随中断。
+2. **相关代码实现**
+   - 任务管理器`TaskManager`:是进程调度的容器,包含就绪队列`ready_queue`和可中断睡眠状态队列`interruptible_queue`。其数据结构和方法如下:
+     - 数据结构:
+
+```rust
+pub struct TaskManager {
+    pub ready_queue: VecDeque<Arc<TaskControlBlock>>,
+    pub interruptible_queue: VecDeque<Arc<TaskControlBlock>>,
+}
+```
+
+- **方法**:
+
+  - `new`:初始化`TaskManager`实例,返回包含两个空队列的实例。
+  - `add`:将进程添加到就绪队列。
+  - `fetch`:从就绪队列获取一个进程,若有`oom_handler`特性,获取进程时会将其标记为活跃状态。
+  - `add_interruptible`:将可中断进程添加到可中断队列。
+  - `drop_interruptible`:从可中断队列移除指定进程。
+  - `find_by_pid`和`find_by_tgid`:根据进程 ID 或线程组 ID 在队列中查找进程。
+  - `ready_count`和`interruptible_count`:获取就绪队列和可中断队列中进程的数量。
+  - `wake_interruptible`:将已唤醒的可中断进程移动到就绪队列,内部调用`try_wake_interruptible`。
+  - `try_wake_interruptible`:尝试将已唤醒的可中断进程移动到就绪队列,若进程不存在于就绪队列,则添加;若已存在,返回错误。
+
+- **等待队列**`WaitQueue`
+
+  :用于管理等待特定资源或时间的进程或线程,是先进先出的数据结构。其方法如下:
+
+  - `new`:创建新的`WaitQueue`实例。
+  - `add_task`:将任务添加到等待队列末尾。
+  - `pop_task`:从等待队列前端弹出任务。
+  - `contains`:判断等待队列是否包含给定任务。
+  - `is_empty`:判断等待队列是否为空。
+  - `wake_all`:唤醒等待队列中所有任务,实际通过调用`wake_at_most(usize::MAX)`实现。
+  - `wake_at_most`:唤醒等待队列中不超过`limit`数量的任务,从队列头部弹出任务,尝试升级任务引用,根据任务状态处理(`Interruptible`状态的任务将状态改为`Ready`),并通过`try_wake_interruptible`尝试唤醒任务,记录唤醒数量,若达到`limit`则停止。
+
+- **超时等待池**`TimeoutWaitQueue`
+
+  :用于处理等待超时情况,允许进程或线程设置超时时间,超时未唤醒则触发超时逻辑。其方法如下:
+
+  - `new`:创建新的`TimeoutWaitQueue`实例。
+  - `add_task`:将带有超时时间的任务添加到超时等待队列,将任务包装成`TimeoutWaiter`结构并按超时时间顺序插入二进制堆。
+  - `wake_expired`:唤醒超时等待队列中已过期的任务,获取`TASK_MANAGER`锁,遍历任务列表,检查任务超时时间,未过期则放回队列,过期则尝试升级任务引用,若任务存在且状态为`Interruptible`,则改为`Ready`,并通过`wake_interruptible`唤醒任务。
+
+#### 进程退出
+
+1. `sys_exit`**系统调用**
+   - 当应用调用`sys_exit`系统调用主动退出或执行出错由内核终止时,内核调用`exit_current_and_run_next`函数退出当前进程并切换到下一个进程。`exit_current_and_run_next`函数以退出码为参数,将当前进程状态改为`Zombie`,写入退出码到`TCB`,将当前进程的子进程挂到初始进程`initproc`下,清空子进程向量,回收当前进程占用的部分资源(调用`recycle_data_pages`函数清空地址空间中的逻辑段列表),最后调用`schedule`触发调度与任务切换。
+   - 示例代码:
+
+```rust
+pub fn sys_exit(exit_code: i32) ->! {
+    exit_current_and_run_next(exit_code);
+    panic!("Unreachable in sys_exit!");
+}
+
+pub fn exit_current_and_run_next(exit_code: i32) {
+    let task = take_current_task().unwrap();
+    let mut inner = task.inner_exclusive_access();
+    inner.task_status = TaskStatus::Zombie;
+    inner.exit_code = exit_code;
+    {
+        let mut initproc_inner = INITPROC.inner_exclusive_access();
+        for child in inner.children.iter() {
+            child.inner_exclusive_access().parent = Some(Arc::downgrade(&INITPROC));
+            initproc_inner.children.push(child.clone());
+        }
+    }
+    inner.children.clear();
+    inner.memory_set.recycle_data_pages();
+    drop(inner);
+    drop(task);
+    let mut _unused = TaskContext::zero_init();
+    schedule(&mut _unused as *mut _);
+}
+
+impl MemorySet {
+    pub fn recycle_data_pages(&mut self) {
+        self.areas.clear();
+    }
+}
+```
+
+   2.`sys_wait4`**系统调用(父进程回收子进程资源)**
+
+- `sys_wait4`函数用于父进程回收子进程资源,其参数包括指定进程的`pid`、存储退出码的用户空间区域指针`status`、函数执行方式选项`option`和存储进程资源占用信息的`ru`指针。函数逻辑如下:
+
+  - 判断当前进程是否有符合要求的子进程(根据传入的`pid`判断),若无则返回`ECHILD`。
+
+  - 若有符合要求的子进程,判断其中是否有僵尸进程,若有则记录其在子进程向量中的下标;若无则根据`option`中的`WNOHANG`标志处理(若有该标志,返回`SUCCESS`;若无则切换至下一个进程执行)。
+
+  - 将僵尸子进程从向量中移除,确认这是对该子进程控制块的唯一一次强引用,然后收集子进程信息(如退出码等),最后以回收的子进程`PID`作为函数返回值返回。
+
+- 示例代码:
+
+```rust
+pub fn sys_wait4(pid: isize, status: *mut u32, option: u32, ru: *mut Rusage) -> isize {
+    let option = WaitOption::from_bits(option).unwrap();
+    info!("[sys_wait4] pid: {}, option: {:?}", pid, option);
+    let task = current_task().unwrap();
+    let token = task.get_user_token();
+    loop {
+        // find a child process
+        let mut inner = task.acquire_inner_lock();
+        if inner
+          .children
+          .iter()
+          .find(|p| pid == -1 || pid as usize == p.getpid())
+          .is_none()
+        {
+            return ECHILD;
+        }
+        inner
+          .children
+          .iter()
+          .filter(|p| pid == -1 || pid as usize == p.getpid())
+          .for_each(|p| {
+                trace!(
+                    "[sys_wait4] found child pid: {}, status: {:?}",
+                    p.pid.0,
+                    p.acquire_inner_lock().task_status
+                );
+            });
+        let pair = inner.children.iter().enumerate().find(|(_, p)| {
+            // ++++ temporarily hold child PCB lock
+            pid == -1 || pid as usize == p.getpid()
+        });
+        if let Some((idx, _)) = pair {
+            // drop last TCB of child
+            let child = inner.children.remove(idx);
+            // confirm that child will be deallocated after being removed from children list
+            assert_eq!(Arc::strong_count(&child), 1);
+            // if main thread exit
+            if child.pid.0 == child.tgid {
+                let found_pid = child.getpid();
+                // ++++ temporarily hold child lock
+                let exit_code = child.acquire_inner_lock().exit_code;
+                // ++++ release child PCB lock
+                if!status.is_null() {
+                    // this may NULL!!!
+                    match translated_refmut(token, status) {
+                        Ok(word) => *word = exit_code,
+                        Err(errno) => return errno,
+                    };
+                }
+                return found_pid as isize;
+            }
+        } else {
+            drop(inner);
+            if option.contains(WaitOption::WNOHANG) {
+                return SUCCESS;
+            } else {
+                block_current_and_run_next();
+                debug!("[sys_wait4] --resumed--");
+            }
+        }
+    }
+}
+```
+
+   3.`sys_kill`**系统调用(进程间的`kill`机制)**
+
+- `sys_kill`系统调用用于一个进程 “杀死” 其他进程,函数接受目的进程的`pid`和信号`sig`两个参数。实现逻辑为找出要 “杀死” 的进程,赋予指定信号量,若进程处于阻塞态则唤醒它,让其自我终结。目前该系统调用仅是初步实现,未最终完善。
+- 示例代码:
+
+```rust
+pub fn sys_kill(pid: usize, sig: usize) -> isize {
+    let signal = match Signals::from_signum(sig) {
+        Ok(signal) => signal,
+        Err(_) => return EINVAL,
+    };
+    if pid > 0 {
+        if let Some(task) = find_task_by_tgid(pid) {
+            if!signal.is_empty() {
+                let mut inner = task.acquire_inner_lock();
+                inner.add_signal(signal);
+                // wake up target process if it is sleeping
+                if inner.task_status == TaskStatus::Interruptible {
+                    inner.task_status = TaskStatus::Ready;
+                    drop(inner);
+                    wake_interruptible(task);
+                }
+            }
+            SUCCESS
+        } else {
+            ESRCH
+        }
+    } else if pid == 0 {
+        todo!()
+    } else if (pid as isize) == -1 {
+        todo!()
+    } else { // (pid as isize) < -1
+        todo!()
+    }
+}
+```
+
+### 文件系统
+
+#### 概述
+
+1. **文件抽象**
+   - **文件操作接口定义**:在`os/fs/file_trait.rs`中定义了文件操作的接口`File` trait,包含了一系列方法,如`deep_clone`用于深度复制文件,`readable`和`writable`判断文件是否可读可写,`read`和`write`实现文件的读写操作,`get_size`、`get_stat`、`get_file_type`获取文件的基本信息(大小、统计信息、文件类型)等。这些方法为文件操作提供了统一的抽象,不同的文件系统实现可以根据自身特点来具体实现这些方法。
+   - **文件结构实现**:以`OSInode`为例(在`os/fs/inode.rs`中定义),它实现了`File` trait,其结构包含`readable`、`writable`、`special_use`、`append`等字段,用于表示文件的读写属性、特殊用途和追加模式等。`inner`字段指向`Inode`,记录文件详细内容,通过`Arc`指针实现硬链接机制,确保多个`OSInode`可指向相同的`InodeImpl`,并且在所有硬链接释放后,文件信息才被彻底释放。
+2. **文件系统的目录结构**
+   - **目录树节点结构**:文件结构`OSInode`中的`dirnode_ptr`指向`DirectoryTreeNode`目录树节点结构(在`os/fs/directory_tree.rs`中定义),该结构本质上是一种 Inode 节点,内部记录了目录字符串指向的文件信息,包括`spe_usage`(用于记录目录的使用情况)、`name`(目录名称)、`filesystem`(指向所属文件系统)、`file`(指向对应文件)、`selfptr`和`father`(用于构建目录树结构)、`children`(用于存储子目录或文件节点)等字段。
+   - **目录节点向量与根目录节点**:内核全局维护了`DIRECTORY_VEC`目录节点向量(在`os/fs/directory_tree.rs`中定义),用于记录当前系统所在的目录,同时记录了根目录节点`ROOT`,其目录为空字符串。内核提供了`insert_directory_vec`、`delete_directory_vec`、`update_directory_vec`等接口,用于方便地实现目录跳转等操作,基于这些方法,衍生出了一系列基本文件操作方法,如`mkdir`、`cd`、`open`、`create`、`delete`、`rename`等。
+3. **虚拟文件系统**
+   - **多层次文件系统结构**
+     - **磁盘块设备接口层**:在`easy-fs/src/block_dev.rs`中定义了`BlockDevice` trait,提供了对块设备进行读写的操作接口,包括`read_block`和`write_block`方法,分别用于从块设备读取数据到内存缓冲区和将内存缓冲区数据写回块设备,数据以块为单位进行读写。NPUcore 通过 K210 依赖中的 GPIO 端口 SPI 串口底层支持,在`os/src/drivers/block/sdcard.rs`中对`SDCardWrapper`实现了`BlockDevice` trait,实现了对 SD 卡的直接读写操作,并设置了 I/O 故障时的最大重试次数为 5。
+     - **块缓存层**:位于`easy-fs/src/block_cache.rs`,主要目的是提高文件系统性能和效率。通过在内存中缓存磁盘块,减少磁盘 I/O 操作,加速数据访问,合并写操作,减少重复数据读取,提供一致性和同步机制,支持异步写操作,减轻磁盘负载。定义了`Cache` trait 用于缓存对象的基本操作,包括`read`、`modify`和`sync`方法,分别用于只读访问缓存、修改缓存和将缓存数据写回磁盘。`CacheManager` trait 用于管理缓存,`BlockCacheManager`和`PageCacheManager` trait 进一步封装了`CacheManager`,分别用于管理块缓存和页缓存,提供了获取缓存块的方法,如`try_get_block_cache`和`get_block_cache`。
+     - **磁盘数据结构层**:NPUcore 的`easy-fs`文件系统使用 fat32 实现,该层包含管理文件系统的超级块、索引节点位图区、数据块位图区、索引节点区和数据块区等。在`layout.rs`中描述了相关 fat 文件系统所需的数据结构,如`BPB`、`FSInfo`、`FATDirEnt`等,`bitmap.rs`中定义了`FAT`结构体用于维护 fat 表,实现了如`get_next_clus_num`、`get_all_clus_num`、`alloc`、`free`等方法,用于管理簇的分配和释放。
+     - **简单文件系统层**:位于`efs.rs`,逻辑上介于 VFS 和 fat32 文件系统之间,用于维护数据区,方便 VFS 操作。定义了`EasyFileSystem`结构体,包含`block_device`(块设备指针)、`fat`(fat 指针)、`data_area_start_block`(数据区开始的磁盘编号)、`root_clus`(根目录的簇号)、`sec_per_clus`(每个簇的扇区数)、`byts_per_sec`(每个扇区的字节数)等字段,实现了`first_sector_of_cluster`(计算给定簇的第一个扇区编号)、`open`(打开文件系统)、`alloc_blocks`(分配新的块)等方法。
+     - **虚拟文件系统层**:位于文件系统层次的最高层,提供文件和 I 节点的抽象,可直接被内核操作,屏蔽不同文件系统的差异,为上层提供统一接口。在`vfs.rs`中定义了相关数据结构,如`FileContent`用于描述文件内容,包含`size`(文件大小)、`clus_list`(文件包含的簇号)、`file_cache_mgr`(文件的 cache 管理器)、`hint`(维护目录文件的特殊目录项)等字段;`Inode`用于描述文件信息,包含`inode_lock`(读写锁)、`file_content`(指向文件内容)、`file_cache_mgr`(指向管理该 inode 的 PageCacheManager)、`file_type`(文件类型)、`parent_dir`(指向父目录的 inode 及位置)、`fs`(指向所属简单文件系统)、`time`(文件相关时间属性)、`deleted`(标识是否释放文件内容区域)等字段,并实现了文件创建、删除、读写、修改大小、遍历和查询等操作方法;`DirIter`是用于目录项的迭代器,`DirWalker`是基于`DirIter`的目录文件迭代器,用于迭代目录条目。
+   - **VFS** **接口**
+     - **超级块**:用于保存文件系统的所有元数据,是文件系统的信息库,代表一个文件系统,文件系统的元数据修改会反映在超级块上,超级块对象常驻内存并被缓存,以链式方式维护,为所有进程可见。
+     - **目录项**:管理路径的目录项,存储目录下文件的 inode 号和文件名等信息,内部为树形结构,操作系统通过从根目录开始按层次解析路径来检索文件。
+     - **inode**:存放具体文件的一般信息,是文件的唯一标识,一个 inode 对应一个文件,通过 inode 可找到文件在磁盘扇区的位置,inode 模块可链接到 address_space 模块,方便查找文件数据是否已缓存。
+     - **打开文件列表模块**:包含所有内核已打开的文件,打开的文件对象由 open 系统调用在内核中创建,也叫文件句柄,列表中的每个表项是一个结构体`struct file`,存储文件的各种状态参数。
+     - **file_operations**:模块维护一个数据结构,是一系列函数指针的集合,包含所有可使用的系统调用函数,如 open、read、write、mmap 等,每个打开文件可通过连接到该模块,实现对文件的各种操作。
+     - **address_space**:表示文件在页缓存中已缓存的物理页,是页缓存和外部设备中文件系统的桥梁,关联了内存系统和文件系统,内部维护树结构指向物理页结构`page`,同时通过`host`指针指向 inode 获取文件元数据。
+4. **文件共享与** **PIPE**
+   - **软链接与硬链接**:在 NPUcore 中,`OSInode`包含`inner: Arc<InodeImpl>`,指向`Inode`,实现了 Linux 的硬链接。多个`OSInode`可指向相同的`InodeImpl`,文件数据空间只占用一份。`OSInode`实现了`Drop` trait,当硬链接全部删除后,`drop`方法会减少目录项的引用计数,计数为 0 时触发回收机制,释放文件空间。
+   - **管道机制**
+     - **管道定义与结构**:管道用于在具有父子关系的进程间传递数据,在 NPUcore 中,管道的原型为`Pipe`结构体(在`os/src/process/pipe.rs`中定义),包含`readable`和`writable`字段表示管道端的读写属性,`buffer`字段指向`PipeRingBuffer`管道自身,`PipeRingBuffer`是一个固定长度为 32 的`u8`循环队列,包含`arr`(数据数组)、`head`(头指针)、`tail`(尾指针)、`status`(队列状态)、`write_end`(写端弱引用数目)等字段,通过头尾指针判断队列状态。
+     - **管道创建与系统调用**:`make_pipe`函数创建管道,返回管道的两端(读端和写端)。`sys_pipe`系统调用通过传入空白文件描述符地址`pipe`,为其创建管道、写端与读端的文件描述符,将文件描述符存入进程控制块的文件描述符列表中,并将地址写入`pipe`。管道两端实现了`File` trait,可像文件一样进行读写操作。
+     - **管道缓冲区读写**:管道缓冲区读写方法类似循环队列的`pop`和`push`操作,`write_byte`方法向缓冲区写入数据,`read_byte`方法从缓冲区读取数据,同时定义了`available_read`和`available_write`方法分别指出当前可读和可写的数目,`all_write_ends_closed`方法判断管道写端是否全部关闭。
+     - **管道关闭**:通过系统调用`sys_close`关闭管道的两端,将文件描述符标记为空,导致`Arc`引用被销毁,当管道两端都关闭后,`buffer`的引用计数为 0,管道自身被销毁。
+
+#### 文件相关系统调用代码及解释
+
+1. ##### **open** **系统调用**
+
+   - **用户态代码**
+
+     - 在`user/src/syscall.rs`中,`sys_open`函数用于发起打开文件的系统调用,它接受文件名`path`和打开标志`flags`作为参数,将其封装后通过`syscall`函数传递给内核。
+
+     ```rust
+     pub fn sys_open(path: &str, flags: u32) -> isize {
+         syscall(SYSCALL_OPEN, [path.as_ptr() as usize, flags as usize, 0])
+     }
+     ```
+
+     - 在`user/src/lib.rs`中,使用`bitflags!`宏定义了`OpenFlags`结构体,将`u32`类型的`flags`包装为更易使用的结构体,方便设置和检查打开文件的模式,如只读、只写、读写、追加、创建、截断等。
+
+     ```rust
+     bitflags! {
+         pub struct OpenFlags: u32 {
+             const RDONLY = 0;
+             const WRONLY = 1 << 0;
+             const RDWR = 1 << 1;
+             const CREATE = 1 << 6;
+             const TRUNC = 1 << 9;
+         }
+     }
+     ```
+
+     - 在`user/src/user_call.rs`中,`open`函数是对`sys_open`的进一步封装,将用户传入的`OpenFlags`类型的`flags`转换为`u32`后传递给`sys_open`,使得用户可以更方便地使用打开文件的功能。
+
+     ```rust
+     pub fn open(path: &str, flags: OpenFlags) -> isize {
+         sys_open(path, flags.bits)
+     }
+     ```
+
+   - **内核态代码**:在`os/src/syscall/mod.rs`中,`sys_open`系统调用会调用`sys_openat`函数,并传入`AT_FDCWD`(当前工作目录)、文件名、打开标志和默认权限`0o777u32`。
+
+     `sys_openat`函数会根据传入的参数选择要打开的文件描述符,并尝试打开文件,如果成功则将新打开的文件描述符插入到当前任务的文件描述符表中,并返回新文件描述符的整数值;如果失败则返回相应的错误码。
+
+     ```rust
+     SYSCALL_OPEN => sys_openat(AT_FDCWD, args[0] as *const u8, args[1] as u32, 0o777u32),
+     ```
+
+2. ##### **close** **系统调用**
+
+   - **用户态代码**:在`user/src/syscall.rs`中,`sys_close`函数接受文件描述符`fd`作为参数,通过`syscall`函数将关闭文件的请求传递给内核。
+
+     ```rust
+     pub fn sys_close(fd: usize) -> isize {
+         syscall(SYSCALL_CLOSE, [fd])
+     }
+     ```
+
+   - **内核态代码**:在`os/src/syscall/mod.rs`中,`sys_close`函数首先获取当前任务的引用,然后获取任务的文件表锁,通过文件描述符`fd`从文件表中获取文件描述符的引用。如果获取成功,将文件描述符表中对应的项设置为`None`,释放资源并返回`SUCCESS`;如果获取失败,则返回相应的错误码。
+
+     ```rust
+     pub fn sys_close(fd: usize) -> isize {
+         info!("[sys_close] fd: {}", fd);
+         let task = current_task().unwrap();
+         let mut fd_table = task.files.lock();
+         match fd_table.remove(fd) {
+             Ok(_) => SUCCESS,
+             Err(errno) => errno,
+         }
+     }
+     ```
+
+3. ##### **read** **系统调用**
+
+   - **用户态代码**:在`user/src/syscall.rs`中,`sys_read`函数接受文件描述符`fd`、缓冲区`buf`和要读取的字节数`count`作为参数,通过`syscall`函数将读取文件数据的请求传递给内核。
+
+     ```rust
+     pub fn sys_read(fd: usize, buf: usize, count: usize) -> isize {
+         syscall(SYSCALL_READ, [fd, buf, count])
+     }
+     ```
+
+   - **内核态代码**
+
+     - 在`os/src/syscall/mod.rs`中,`sys_read`函数首先获取当前任务的引用,然后获取任务的文件表锁,通过文件描述符`fd`从文件表中获取文件描述符的引用。如果文件不可读,则返回错误码`EBADF`。接着获取当前任务的用户令牌,将用户空间的缓冲区地址和长度转换为内核可以操作的`UserBuffer`对象,然后调用文件描述符的`read_user`方法从文件中读取数据到用户提供的缓冲区中,成功时返回读取的字节数,失败时返回相应的错误码。
+
+     ```rust
+     pub fn sys_read(fd: usize, buf: usize, count: usize) -> isize {
+         let task = current_task().unwrap();
+         let fd_table = task.files.lock();
+         let file_descriptor = match fd_table.get_ref(fd) {
+             Ok(file_descriptor) => file_descriptor,
+             Err(errno) => return errno,
+         };
+         // fd is not open for reading
+         if!file_descriptor.readable() {
+             return EBADF;
+         }
+         let token = task.get_user_token();
+         file_descriptor.read_user(
+             None,
+             UserBuffer::new({
+                 match translated_byte_buffer(token, buf as *const u8, count) {
+                     Ok(buffer) => buffer,
+                     Err(errno) => return errno,
+                 }
+             }),
+         ) as isize
+     }
+     ```
+
+4. ##### **write** **系统调用**
+
+   - **用户态代码**:在`user/src/syscall.rs`中,`sys_write`函数接受文件描述符`fd`、缓冲区`buf`和要写入的字节数`count`作为参数,通过`syscall`函数将写入文件数据的请求传递给内核。
+
+     ```rust
+     pub fn sys_write(fd: usize, buf: usize, count: usize) -> isize {
+         syscall(SYSCALL_WRITE, [fd, buf, count])
+     }
+     ```
+
+   - **内核态代码**
+
+     - 在`os/src/syscall/mod.rs`中,`sys_write`函数首先获取当前任务的引用,然后获取任务的文件表锁,通过文件描述符`fd`从文件表中获取文件描述符的引用。如果文件不可写,则返回错误码`EBADF`。接着获取当前任务的用户令牌,将用户空间的缓冲区地址和长度转换为内核可以操作的`UserBuffer`对象,然后调用文件描述符的`write_user`方法将用户提供的缓冲区中的数据写入到与文件描述符对应的文件中,成功时返回写入的字节数,失败时返回相应的错误码。
+
+     ```rust
+     pub fn sys_write(fd: usize, buf: usize, count: usize) -> isize {
+         let task = current_task().unwrap();
+         let fd_table = task.files.lock();
+         let file_descriptor = match fd_table.get_ref(fd) {
+             Ok(file_descriptor) => file_descriptor,
+             Err(errno) => return errno,
+         };
+         if!file_descriptor.writable() {
+             return EBADF;
+         }
+         let token = task.get_user_token();
+         file_descriptor.write_user(
+             None,
+             UserBuffer::new({
+                 match translated_byte_buffer(token, buf as *const u8, count) {
+                     Ok(buffer) => buffer,
+                     Err(errno) => return errno,
+                 }
+             }),
+         ) as isize
+     }
+     ```
+
+5. ##### **fstat** **和** **fstatat** **系统调用**
+
+   - **用户态代码**
+
+     - 在`user/src/syscall.rs`中,`sys_fstat`函数接受文件描述符`fd`和缓冲区`statbuf`作为参数,用于获取打开文件的状态信息并将其存储到`statbuf`中,通过`syscall`函数将请求传递给内核。
+
+     ```rust
+     pub fn sys_fstat(fd: usize, statbuf: *mut u8) -> isize {
+         syscall(SYSCALL_FSTAT, [fd, statbuf as usize])
+     }
+     ```
+
+     - 在`user/src/syscall.rs`中,`sys_fstatat`函数接受目录文件描述符`dirfd`、文件路径`path`、缓冲区`buf`和标志`flags`作为参数,用于获取指定目录下文件的状态信息并将其存储到`buf`中,通过`syscall`函数将请求传递给内核。
+
+     ```rust
+     pub fn sys_fstatat(dirfd: usize, path: *const u8, buf: *mut u8, flags: u32) -> isize {
+         syscall(SYSCALL_FSTATAT, [dirfd, path as usize, buf as usize, flags])
+     }
+     ```
+
+   - **内核态代码**
+
+     - 在`os/src/syscall/mod.rs`中,`sys_fstat`函数首先获取当前任务的引用,然后获取任务的用户令牌。接着根据文件描述符`fd`获取对应的文件描述符对象,如果`fd`为`AT_FDCWD`(当前工作目录),则获取当前任务文件系统的工作节点;否则从文件描述符表中获取。然后读取文件的状态信息并复制到用户提供的`statbuf`缓冲区中,如果复制失败则返回错误码`EFAULT`,成功则返回`SUCCESS`。
+
+     ```rust
+     pub fn sys_fstat(fd: usize, statbuf: *mut u8) -> isize {
+         let task = current_task().unwrap();
+         let token = task.get_user_token();
+         info!("[sys_fstat] fd: {}", fd);
+         let file_descriptor = match fd {
+             AT_FDCWD => task.fs.lock().working_inode.as_ref().clone(),
+             fd => {
+                 let fd_table = task.files.lock();
+                 match fd_table.get_ref(fd) {
+                     Ok(file_descriptor) => file_descriptor.clone(),
+                     Err(errno) => return errno,
+                 }
+             }
+         };
+         if copy_to_user(token, &file_descriptor.get_stat(), statbuf as *mut Stat).is_err() {
+             log::error!("[sys_fstat] Failed to copy to {:?}", statbuf);
+             return EFAULT;
+         };
+         SUCCESS
+     }
+     ```
+
+     - 在`os/src/syscall/mod.rs`中,`sys_fstatat`函数首先获取当前任务的用户令牌,将用户提供的文件路径`path`从用户空间地址转换为内核空间可处理的格式,如果转换失败则返回错误码。接着解析用户提供的标志`flags`,如果包含未知位则记录警告并返回错误码。然后根据目录文件描述符`dirfd`获取对应的文件描述符对象,如果`dirfd`为`AT_FDCWD`(当前工作目录),则获取当前任务文件系统的工作节点;否则从文件描述符表中获取。接着尝试以只读方式打开文件,如果文件不存在或无法打开则返回相应错误码,成功打开后获取文件的状态信息并复制到用户提供的`buf`缓冲区中,如果复制失败则返回错误码`EFAULT`,成功则返回`SUCCESS`。
+
+     ```rust
+     pub fn sys_fstatat(dirfd: usize, path: *const u8, buf: *mut u8, flags: u32) -> isize {
+         let token = current_user_token();
+         let path = match translated_str(token, path) {
+             Ok(path) => path,
+             Err(errno) => return errno,
+         };
+         let flags = match FstatatFlags::from_bits(flags) {
+             Some(flags) => flags,
+             None => {
+                 warn!("[sys_fstatat] unknown flags");
+                 return EINVAL;
+             }
+         };
+         info!(
+             "[sys_fstatat] dirfd: {}, path: {:?}, flags: {:?}",
+             dirfd as isize, path, flags,
+         );
+         let task = current_task().unwrap();
+         let file_descriptor = match dirfd {
+             AT_FDCWD => task.fs.lock().working_inode.as_ref().clone(),
+             fd => {
+                 let fd_table = task.files.lock();
+                 match fd_table.get_ref(fd) {
+                     Ok(file_descriptor) => file_descriptor.clone(),
+                     Err(errno) => return errno,
+                 }
+             }
+         };
+         match file_descriptor.open(&path, OpenFlags::O_RDONLY, false) {
+             Ok(file_descriptor) => {
+                 if copy_to_user(token, &file_descriptor.get_stat(), buf as *mut Stat).is_err() {
+                     log::error!("[sys_fstatat] Failed to copy to {:?}", buf);
+                     return EFAULT;
+                 };
+                 SUCCESS
+             }
+             Err(errno) => errno,
+         }
+     }
+     ```
+
+#### 虚拟文件系统及接口代码及解释
+
+1. ##### **VFS** **接口实现**
+
+   - **sys_openat** **接口**:在`os/src/syscall/mod.rs`中,`sys_openat`函数接收目录描述符`dirfd`、路径`path`、打开标志位`flags`和文件权限`mode`作为参数。它根据`dirfd`选择要打开的文件描述符,通过文件描述符的`open`方法尝试打开文件。如果打开成功,将新打开的文件描述符插入到当前任务的文件描述符表中,并返回新文件描述符的整数值;如果失败,则返回相应的错误码。
+
+     ```rust
+     pub fn sys_openat(dirfd: usize, path: *const u8, flags: u32, mode: u32) -> isize {
+         // 函数体实现
+     }
+     ```
+
+   - **sys_close** **接口**:在`os/src/syscall/mod.rs`中,`sys_close`函数接受文件描述符`fd`作为参数,将进程控制块中的文件描述符表中对应的项设置为`None`,释放资源。这会导致内层的引用计数类型`Arc`被销毁,减少文件的引用计数,当引用计数为 0 时,文件所占用的资源会被自动回收。
+
+     ```rust
+     pub fn sys_close(fd: usize) -> isize {
+         // 函数体实现
+     }
+     ```
+
+   - **sys_read** **接口**:在`os/src/syscall/mod.rs`中,`sys_read`函数根据文件描述符`fd`在文件描述符表中找到相应的文件描述符对象,然后使用文件描述符的`read_user`方法尝试从文件中读取数据到用户空间的缓冲区`buf`中,读取的字节数由`count`指定。
+
+     ```rust
+     pub fn sys_read(fd: usize, buf: usize, count: usize) -> isize {
+         // 函数体实现
+     }
+     ```
+
+   - **sys_write** **接口**:在`os/src/syscall/mod.rs`中,`sys_write`函数根据文件描述符`fd`在文件描述符表中找到相应的文件描述符对象,然后使用文件描述符的`write_user`方法尝试将用户空间缓冲区`buf`中的数据写入到文件中,写入的字节数由`count`
+
+     指定。
+
+     ```rust
+     pub fn sys_write(fd: usize, buf: usize, count: usize) -> isize {
+         // 函数体实现
+     }
+     ```
+
+   - **sys_fstat** **接口**:在`os/src/syscall/mod.rs`中,`sys_fstat`函数根据文件描述符`fd`在文件描述符表中找到相应的文件描述符对象,然后通过文件描述符提供的方法获取文件信息,并将其写入缓冲区`statbuf`中。
+
+     ```rust
+     pub fn sys_fstat(fd: usize, statbuf: *mut u8) -> isize {
+         // 函数体实现
+   }
+     ```
+
+   - **sys_mount** **接口**:在`os/src/syscall/mod.rs`中,`sys_mount`函数实现了文件系统的挂载功能,接收要挂载的文件系统的源路径或标识`source`、挂载目标位置`target`、文件系统类型`filesystemtype`、挂载选项和标志`mountflags`以及挂载所需的其他数据`data`作为参数。通过这些参数,函数执行挂载操作,将指定的文件系统挂载到目标位置。
+   
+     ```rust
+   pub fn sys_mount(source: *const u8, target: *const u8, filesystemtype: *const u8, mountflags: usize, data: *const u8,) -> isize {
+         // 函数体实现
+     }
+     ```
+   
+   - **sys_lseek** **接口**:在`os/src/syscall/mod.rs`中,`sys_lseek`函数根据文件描述符`fd`在文件描述符表中找到相应的文件描述符对象,然后以`whence`为偏移的基准,`offset`为偏移量,对文件指针进行定位操作,并返回操作后的文件指针位置。
+
+     ```rust
+   pub fn sys_lseek(fd: usize, offset: isize, whence: u32) -> isize {
+         // 函数体实现
+     }
+     ```
+
+   - **sys_mkdirat 接口**:在`os/src/syscall/mod.rs`中,`sys_mkdirat`函数接受目录文件描述符`dirfd`、路径`path`和文件权限`mode`作为参数,用于在指定目录下创建新的目录。函数根据`dirfd`找到相应的目录,然后在该目录下创建名为`path`的新目录,权限由`mode`指定。如果创建成功,返回`SUCCESS`;如果失败,返回相应的错误码。
+
+## 重要系统调用解释
+
+### sbrk系统调用
+
+功能概述:
+
+> sbrk 系统调用主要用于动态调整进程的堆空间大小。它可以根据传入的参数,增加或缩小堆的内存区域,为进程提供灵活的内存管理功能。
+
+函数原型与参数含义:
+
+> 1.函数原型:
+> pub fn sbrk(&mut self, heap_pt: usize, heap_bottom: usize, increment: isize) -> usize
+> 2.参数含义:
+> self : 所在的地址空间MemorySet
+> heap_pt : 当前堆顶指针
+> heap_bottom : 堆底指针
+> increment : 堆空间大小的变化量,可正可负可零
+> 返回值 : 改变后新的堆顶指针
+
+实现原理与相关函数调用:
+
+> 1.实现依赖:sbrk 的实现依赖于 MemorySet 接口下的 mmap 和 munmap 函数。mmap 用于扩大堆空间,它能够将文件或匿名内存映射到进程的地址空间;munmap 用于缩小堆空间,取消已建立的内存映射。
+> 2.核心逻辑:在 sbrk 函数中,首先判断increment的正负性。如果increment大于 0,会进一步检查扩大后的堆地址是否超过进程地址空间限制。若未超过,则调用 mmap 函数进行堆空间的扩大操作,并返回新的堆顶指针;若超过限制,则返回原堆顶指针。当increment小于 0 时,会检查缩小后的堆地址是否小于堆底指针。若不小于,则调用 munmap 函数进行堆空间的缩小操作;若小于,则不进行缩小操作,直接返回原堆顶指针。
+
+**一定要注意下面的参数传递:**
+
+![img](http://10.68.28.248:1006/profile/upload/2024/12/23/acceffdede0bbe0038de036424a1cfc1.png)
+
+这样传递参数可以保证不论是增加还是减少堆空间,均保证堆顶在堆底上面,刚开始就是这里没写对,导致当increment为负数时出错。
+
+### mmap系统调用
+
+功能概述:
+
+> mmap 系统调用用于将文件映射到进程的地址空间中,实现进程对文件的直接读写操作,就像读写内存一样。它还可以创建匿名内存映射,用于进程间共享内存或进程内部的高效内存管理。
+
+函数签名与参数含义:
+
+> 函数签名:pub fn sys_mmap(start: usize, len: usize, prot: usize, flags: usize, fd: usize, offset: usize) -> isize
+> 参数含义
+> start:指向欲映射的内存起始地址,通常设为 NULL,让系统自动选定地址,映射成功后返回该地址。
+> len:代表将文件中多大的部分映射到内存。
+> prot:映射区域的保护方式,如可读、可写、可执行等。
+> flags:用于指定映射区域的特性,如 MAP_SHARED(共享映射)、MAP_PRIVATE(私有映射)、MAP_ANONYMOUS(创建匿名映射)等。
+> fd:要映射到内存中的文件描述符,使用匿名内存映射时设为 -1。
+> offset:文件映射的偏移量,通常设置为 0,必须是分页大小的整数倍。offset:文件映射的偏移量,通常设置为 0,必须是分页大小的整数倍。
+
+实现过程与步骤:
+
+> 1.找到最后一次 mmap 映射区域:通过查找进程所申请的用户地址空间中最后一个符合要求的 vm_area_struct 结构体,获取其下标,该结构体用于描述进程地址空间中的一个区域,包含起始和结束地址等信息。
+> 2.获取映射区域的起始地址:根据 flags 参数判断是否设置了 MAP_FIXED。如果设置了,会先取消已存在的映射,然后使用用户指定的 start 地址;如果未设置,则根据最后一次 mmap 映射区域的情况确定起始地址。如果设置了 MAP_PRIVATE 或 MAP_ANONYMOUS,且相关条件满足,会将新映射区域与最后一次 mmap 映射区域合并;否则,将最后一次 mmap 映射区域的末尾作为新映射区域的起始地址。最后,根据用户指定的 len 参数确定新映射区域的末尾地址。
+> 3.判断是否是文件映射并处理:如果 flags 参数中不包含 MAP_ANONYMOUS,说明是文件映射。此时,会获取 fd_table,并根据 fd 参数找到对应的文件描述符,检查文件的可读性。如果可读,设置文件偏移量并将相关信息置入新创建的 MapArea 结构体中。
+> 4.将新映射区域加入到 vector 中:将新创建的 MapArea 结构体加入到先前的 vector 中,同时保持 vector 的有序性,确保映射区域按照地址顺序排列。
+
+### sys_getpid系统调用
+
+1.功能概述
+
+> sys_getpid 是一个系统调用函数,其主要功能是获取当前进程的进程 ID(PID)。在 NPUcore 中,每个进程都有唯一的 PID,由内核分配且为正整数,这个 PID 可用于唯一标识进程。
+
+2.执行过程
+
+> 该函数的执行过程相对简单,主要是从内核中检索当前进程的 ID 并返回。
+
+3.返回值
+
+> 若函数成功执行,将返回当前进程的 PID。
+> 若执行失败,则返回 - 1,表示出现错误。
+
+4.应用场景和意义
+
+> 获取进程 ID 是进程间通信和控制的基础。例如,父进程通过 fork () 函数创建子进程后,子进程可以调用 getpid () 获取自己的 PID,进而通过进程间通信方式与父进程交互。
+> 还可以利用进程 ID 向指定进程发送信号,实现对进程的控制。例如,通过 kill () 函数结合 PID 可以向特定进程发送信号来控制其行为。
+
+process.rs文件的第264行添加相应的函数体:
+![img](http://10.68.28.248:1006/profile/upload/2024/12/23/032888aebebe534a369a4401df23f8c1.png)
+
+### getrusage系统调用
+
+1.功能
+
+> getrusage 系统调用的主要功能是获取进程的相关资源信息。这些资源信息包括用户开销时间、系统开销时间、接收的信号量等。
+
+2.相关结构体
+
+> Rusage 结构体定义了获取资源信息的相关字段。
+> ru_utime:用于记录用户 CPU 时间。
+> ru_stime:用于记录系统 CPU 时间。
+> 还有多个未实现(NOT IMPLEMENTED)的字段,如ru_maxrss(最大驻留集大小)、ru_ixrss(共享内存大小积分)等,这些字段目前没有具体实现,但代表了可以获取的潜在资源信息。
+
+3.参数
+
+> who:
+> 当who为 0 时,表示获取当前进程的资源信息。
+> 当who为 - 1 时,表示获取子进程的资源信息,但这部分可以不实现。
+> usage:这是一个指向存放资源使用信息的 Rusage 结构体指针,用于存储获取到的资源信息。
+
+4.意义和用途
+
+> 通过 getrusage 系统调用,操作系统可以方便地获取进程在运行过程中的各种资源消耗情况。这些信息对于系统性能分析、资源管理和优化等方面具有重要意义。例如,通过了解用户 CPU 时间和系统 CPU 时间,可以分析进程在用户态和内核态的运行效率;通过接收的信号量等信息,可以掌握进程的外部交互情况等。
+
+补充os/src/syscall/process.rs文件内第1017行处的getrusage系统调用函数体:
+
+![img](http://10.68.28.248:1006/profile/upload/2024/12/23/d7256aa5967bec0f1001aa384ab4d5cd.png)
+
+### fork与exec系统调用
+
+一、fork 系统调用
+1.作用
+
+> 在 NPUcore 中,除了内核加载的第一个初始进程 initproc 外,其余所有进程均由初始进程 initproc 通过 fork 系统调用创建。也就是说,fork 用于创建新的进程,新进程是调用 fork 的进程(父进程)的副本。
+
+2.调用时机
+
+> 它在创建新进程的过程中被调用,位于初始进程 initproc 已经创建之后,是创建新进程的第二步操作。
+
+3.原理
+
+> 调用 fork 后,新进程会复制父进程的大部分资源,包括内存空间、打开的文件描述符等。新进程和父进程几乎拥有相同的内容,但它们有不同的进程标识符(PID)。
+
+4.与其他操作的关系
+
+> 新进程创建后,还需要进一步的操作才能使其独立运行。因为新进程此时只是父进程的一个副本,并没有自己独立的程序代码和数据文件,这就需要后续的 exec 系统调用。
+
+二、exec 系统调用
+1.作用
+
+> 在调用 fork 创建新进程后,exec 系统调用用于让新进程加载独立的程序代码和数据文件。它会用新的程序替换当前进程的代码段、数据段等内容,使新进程开始执行新的程序。
+
+2.调用时机
+
+> 在通过 fork 创建新进程之后,新进程需要加载自己的程序代码和数据时调用 exec 系统调用。
+
+3.原理
+
+> exec 会加载指定的可执行文件,并重新初始化进程的执行环境。它不会创建新的进程,而是改变现有进程的执行路径,使其开始执行新的程序。
+
+4.与其他操作的关系
+
+> 与 fork 系统调用紧密相关,fork 负责创建新进程,而 exec 负责让新进程加载并执行新的程序。二者结合实现了从现有进程创建新进程并使其执行特定程序的完整过程。
+
+补充os/src/syscall/process.rs文件内第487行处的sys_fork系统调用函数体:
+
+![img](http://10.68.28.248:1006/profile/upload/2024/12/23/c3ce73a40769f05392855ab52bb40f15.png)
+
+补充os/src/syscall/process.rs文件内第495行处的sys_exevce系统调用函数体:
+
+> 这部分较多,就不再截图展示。
+
+### clone系统调用
+
+1.与 fork 的关系及优势
+
+> fork 系统调用是 clone 系统调用在特定参数设置下的简化形式。clone 系统调用相较于 fork,其优势显著。它支持更多参数,这使得它能够在更广泛的场景中发挥作用,提供了更强大的进程创建与管理能力。
+
+2.实现多线程及进程管理优势
+
+> 1.多线程实现机制:
+> 利用 clone 系统调用,内核无需单独构建复杂的多线程机制即可实现 “多线程” 功能。当执行 clone 系统调用时,会创建一个新的 TCB(任务控制块)。
+> 线程与进程的本质区别在于所关联的数据。若新创建的 TCB 所指向的数据与其他实体共享内存空间和信号,那么它就被视为一个线程;反之,则为一个独立的进程。
+> 2.对内核管理的优化:
+> 这种方式简化了内核管理执行态进程的复杂度,将进程(或线程)组织成简单的树状结构,便于内核进行高效的管理与调度。
+> 它严格限制了线程创建的范围,只能创建与当前进程共享上下文的线程,有效防止了远程线程的创建,从而更好地实现了资源隔离。这确保了同一进程内的资源共享高效且安全,同时避免了不同进程间资源的不当交互,增强了系统的稳定性和安全性。
+
+3.clone 系统调用参数解释:
+clone 系统调用在 NPUcore 中的函数签名为:
+
+```rust
+pub fn sys_clone(flags: u32,stack: *const u8,ptid: *mut u32,tls: usize,ctid: *mut u32) -> isize
+```
+
+1.flags 参数
+
+> 结构组成:flags 参数综合了克隆标志 CloneFlags 和子进程结束信号 exit_signal 两部分内容。
+
+2.CloneFlags 具体标志含义:
+
+> 1.CLONE_VM:用于控制父子进程间内存空间的共享模式。若设置,父子进程共享同一内存空间,一方对内存的写操作对另一方可见;若未设置,子进程拥有父进程内存空间的独立副本。
+> 2.CLONE_FS:决定父子进程是否共享文件系统相关设置,包括根目录、当前目录及文件掩码(umask)等。
+> 3.CLONE_FILES:确定父子进程是否共享文件描述符表,影响进程对文件的操作权限和资源共享方式。
+> 4.CLONE_SIGHAND:控制父子进程是否共享信号处理表,决定父子进程对信号的响应和处理方式是否相同。
+> 5.CLONE_THREAD:该标志决定新创建的实体是线程还是独立进程(从线程组角度)。若设置,新实体被视为父进程的线程,与父进程共享线程组 ID(TGID);若未设置,新实体成为独立线程组,其 TGID 等于自身线程 ID(TID)。
+> 6.exit_signal 含义:用于指定子进程退出时向父进程发送的信号。当 flags 的低字节中指定的信号不是SIGCHLD时,父进程在使用wait()等待子进程退出时需指定__WALL或WCLONE选项。若未指定信号(即值为 0),子进程退出后不会向父进程发送信号。
+
+3.stack 参数
+
+> stack 参数的主要作用是指定子进程使用的栈的位置。由于子进程和调用进程在内存管理上存在特殊关系(可能共享内存),子进程不能直接使用调用进程的栈。因此,调用进程必须提前为子进程分配栈内存空间,并将指向该空间起始位置(栈顶)的指针传递给 clone () 函数。需要注意的是,处理器的栈生长方向通常是向下的,即从高地址向低地址生长,所以 stack 参数指向的是为子进程栈分配的内存空间的最高地址。然而,clone () 函数本身并没有提供一种直接的方式让调用者告知内核所分配栈空间的大小。
+
+4.ptid 和 ctid 参数
+
+> 1.与特定 CloneFlags 标志的关联:这两个参数与 CloneFlags 中的CLONE_CHILD_SETTID和CLONE_PARENT_SETTID标志密切相关。
+> 2.参数功能:CLONE_CHILD_SETTID标志使得在 ctid 所指向的内存位置保存新创建子进程(或线程)的线程 ID。此保存操作在 clone 调用返回控制到子进程的用户空间之前完成,但在 clone 调用返回父进程之前,该保存操作的完成情况与CLONE_VM标志相关(可能未完成)。CLONE_PARENT_SETTID标志则在父进程中,将新创建子进程(或线程)的线程 ID 保存到 ptid 所指向的内存位置,保存操作在 clone 调用将控制返回给用户空间之前完成。
+
+5.tls 参数
+
+> tls 参数与 CloneFlags 中的CLONE_SETTLS标志相关。CLONE_SETTLS标志用于将线程本地存储(Thread Local Storage,TLS)的值保存到 tls 字段中。TLS 允许每个线程拥有独立的数据副本,这些数据在整个线程生命周期内都可以被访问,并且对于每个线程都是独立的,不受其他线程的影响。通过这个参数,可以为新创建的子进程(或线程)设置特定的 TLS 值,从而实现线程级别的数据隔离和共享特定于线程的数据。例如,在多线程编程中,不同线程可能需要使用不同的数据库连接对象,这些对象可以存储在 TLS 中,以确保每个线程都能独立地访问和管理自己的数据库连接。
+
+补充os/src/syscall/process.rs文件内第442行处的sys_clone系统调用函数体:
+
+![img](http://10.68.28.248:1006/profile/upload/2024/12/23/ec4dcde9aa88a7f260ad194461eaf1b7.png)
+
+### 文件mmap系统调用的MAP_PRIVATE
+
+![img](http://10.68.28.248:1006/profile/upload/2024/12/23/0e8aff87864f064ee8d596b3c2af1a84.png)
+
+在os/src/mm/memory_set.rs:883处补全mmap系统调用:
+
+![img](http://10.68.28.248:1006/profile/upload/2024/12/23/653b29379c987361d7352cb4c2d0d759.png)
+
+### open系统调用
+
+![img](http://10.68.28.248:1006/profile/upload/2024/12/23/520d755fbbf1abe7b17040f53edb4e43.png)
+
+在os/src/syscall/fs.rs:663处补全openat系统调用:
+
+![img](http://10.68.28.248:1006/profile/upload/2024/12/23/4c3e1fd4faa9adcda7a6fd46d4cae8bc.png)
+
+### fstat系统调用
+
+![img](http://10.68.28.248:1006/profile/upload/2024/12/23/9193406e99126ec3d196e451200b547c.png)
+
+在os/src/syscall/fs.rs:523/527处补全fstat/fstatat系统调用:
+
+![img](http://10.68.28.248:1006/profile/upload/2024/12/23/63e002f30f951c8df78191a37a3d71a0.png)
+
+![img](http://10.68.28.248:1006/profile/upload/2024/12/23/89e436f7481235ee4f572deb21d2bf8f.png)
+
+![img](http://10.68.28.248:1006/profile/upload/2024/12/23/eee67745bee58c9b9c1132065d71aff8.png)
+
+### linkat系统调用
+
+![img](http://10.68.28.248:1006/profile/upload/2024/12/23/4dad7cfc81162166fa6b659567f6fba6.png)
+
+在os/src/syscall/fs.rs:814处补全sys_linkat系统调用:
+![img](http://10.68.28.248:1006/profile/upload/2024/12/23/8ba100d01be055b679fbabded7d0b93b.png)
+
+![img](http://10.68.28.248:1006/profile/upload/2024/12/23/73c4690d6facbd8f31de709c410eb939.png)
+
+### unlinkat系统调用
+
+![img](http://10.68.28.248:1006/profile/upload/2024/12/23/fac21974fcc2a23bdcaa20bf6ac94c6b.png)
+
+在os/src/syscall/fs.rs:815处补全sys_unlinkat系统调用:
+![img](http://10.68.28.248:1006/profile/upload/2024/12/23/2816f004814ffec5e16c24cee1498d2e.png)
+
+![img](http://10.68.28.248:1006/profile/upload/2024/12/23/ac6874c6cb773c415613f4851750633b.png)
+
+### read系统调用与virtio块设备驱动映射
+
+![img](http://10.68.28.248:1006/profile/upload/2024/12/23/eb6a134bfe56140973a47a58bf99d2d8.png)
+
+参照文件os/src/drivers/block/virtio_blk.rs中的write函数完成read()函数:
+![img](http://10.68.28.248:1006/profile/upload/2024/12/23/435812ef08222a594b389fa0ad90c763.png)
+
+![img](http://10.68.28.248:1006/profile/upload/2024/12/23/5688230bd07499f4eb677eb8af155d9b.png)
\ No newline at end of file