-
Notifications
You must be signed in to change notification settings - Fork 439
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
1 changed file
with
117 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
--- | ||
title: 2023开源操作系统训练营第三阶段实习任务总结报告-郝淼 | ||
date: 2023-12-02 19:42:43 | ||
categories: | ||
- oscamp 2023fall arceos unikernel | ||
tags: | ||
- author:MiaoHao-oops | ||
- 2023秋冬季开源操作系统训练营 | ||
- 第三阶段总结报告 | ||
--- | ||
|
||
## 几个核心问题 | ||
|
||
1. 目前 ArceOS 已经实现了“线程”组件 axtask,如何利用它转变为进程? | ||
- 独立的地址空间 | ||
2. 如何保持 unikernel 的特性? | ||
- 仅保留单一特权级 | ||
|
||
## ArceOS 作为动态链接库 | ||
|
||
### 目前成果 | ||
|
||
在保持 ArceOS 单特权级的情况下,支持一些简单的动态链接程序的二进制兼容: | ||
- 编写加载器 loader 支持加载动态链接的 ELF | ||
- 为每个应用创建虚拟地址空间(页表),将其绑定在任务控制块上 | ||
- 每个进程通过任务控制块参与调度,在 `switch_to()` 时切换地址空间 | ||
|
||
### 需要实现的功能 | ||
|
||
1. 动态链接的原理: | ||
- [x] `plt` 和 `got` 如何配合工作: | ||
1. `.rela.plt`:存储了加载器在动态链接时需要填写的 `.got` 表项,以及动态链接函数的**符号信息**,用于索引 `.dynsym`中的一项: | ||
```c | ||
#define ELF64_R_SYM(i) ((i) >> 32) | ||
ELF64_R_SYM(Elf64_Rela.r_info) | ||
``` | ||
2. `.dynsym`:可以查找到和动态链接符号的相关属性,以及**符号名称**(位于 `.dynstr`) | ||
- [x] ABI 是如何保持的:函数签名保持一致即可 | ||
2. ArceOS 需要能加载 ELF | ||
3. 加载时如何填写 .got | ||
4. ArceOS 的编译脚本需要修改, | ||
- [x] 编译 axlibc,导出符号表 | ||
- [x] 自身能够读取 libc 的符号表 | ||
- [ ] 最好能直接将 libc 映射到一个地址段,所有进程共享 | ||
|
||
### 实现路径 | ||
|
||
1. `mmap()`:难度较大,因为目前没有进程,也就没有其对应的地址空间 | ||
2. ELF 解析 | ||
3. 动态链接 | ||
|
||
## 日志记录 | ||
|
||
## 11 月 23 日 | ||
|
||
1. ArceOS 的 axstd 和 axlibc 不能同时链接 | ||
|
||
- 如果强制链接的话会出现重复的符号 | ||
|
||
**解决方法**:将 rust_libc 和 app 链接在一起,c_libc 再和 rust 代码链接 | ||
- [x] rust loader 可以调用 libc 的函数 | ||
- [ ] ArceOS 需要实现 `mmap()` | ||
|
||
### 11 月 27 日 | ||
|
||
KISS 原则实现了加载时的动态链接:先不实现 mmap 和 ELF 解析,采用硬编码的方式加载 hello | ||
|
||
ELF 解析加载过程: | ||
1. 读取 Program Headers(在 host 上用 readelf),将其中类型为 `LOAD` 的 segment 从文件中加载到内存。 | ||
|
||
### 11 月 28 日 | ||
|
||
实现了根据 ELF 头获取需要进行动态链接函数的填充地址和填充值,并进行动态链接。 | ||
|
||
ELF 解析加载过程: | ||
1. 获取 ELF 头,找到 section header table | ||
2. 根据 .shstrtab 找到: | ||
- .rela.plt:找到外部链接符号 | ||
- .rela.dyn:链接内部符号 | ||
- .dynsym:动态链接符号表 | ||
- .dynstr:动态链接符号名的字符串表 | ||
3. 外部动态链接 | ||
- 用 `rela.r_sym()` 索引 `.dynsym`,获取符号项 `dynsym` | ||
- 根据 `dynsym` 判断符号属性,符合动态链接的用 `dynsym.st_name` 索引 `dynstr` 获取符号名 `func_name` | ||
- 根据符号名查找 loader 初始化时填写的链接表,获取链接虚地址 `link_vaddr` | ||
- 将虚地址 `rela.r_offset` 处的 8 字节填写为 `link_vaddr` | ||
4. 内部动态链接 | ||
- `rela.r_addend` 的值即为链接虚地址 `link_vaddr` | ||
- 将虚地址 `rela.r_offset` 处的 8 字节填写为 `link_vaddr` | ||
|
||
### 11 月 29 日 | ||
|
||
根据 ELF 头读取 segment 信息,将其中属性为 LOAD 的段加载到内存。此时对 kernel 而言,hello app 不具有单独的进程(或者线程)属性,而是与 loader 为一体。 | ||
|
||
在 loader 的 Cargo.toml 开启 axstd 的 multitask 属性,可以正常编译,但内核 panic 退出,信息为 `current task is uninitialized`,定位到 `axtask::task::CurrentTask::get()` 函数。显然,在初始化时,已经设置了 `current task`,这里为何报错? | ||
|
||
经过进一步阅读源码,发现 `current task` 的指针通过 `axhal::cpu::current_task_ptr()` 获取,而 `axtask/multitask` 开启了 `percpu` 功能,也就是说此时的 `current task` 指针是一个 `percpu` 变量。目前,ArceOS 中通过 `gp` 实现 `percpu`,这与 riscv 的 ABI 是不符的。具体体现为 hello app 一开始就重新加载了 `gp`,把内核里维护 `percpu` 的值冲掉了。 | ||
|
||
因此,为了保持 ABI,我将 `percpu` 改回了使用 `tp` 维护(前一个修改的 commit 将 `tp` 改为 `gp`)。此时在打开 `axstd/multitask` 后可以正常退出。 | ||
|
||
### 11 月 30 日 | ||
|
||
- [x] 实现 `axtask::spawn_from_ptr()`,专门用于创建从 ELF 加载任务的 TCB | ||
- [x] 在 riscv 的任务上下文中加入 `satp`,`context_switch()` 时同时切换页表 | ||
- [x] 目前实现了动态分配的页表,但未实现进程退出后页的释放 | ||
|
||
### 12 月 1 日 | ||
|
||
通过复用 `axhal::paging::PageTable` 的接口,实现了完整的页表分配、回收功能,并且修改 `TaskInner` 结构体,将页表绑定到上面。这样就实现了类似于 rCore 中的 RAII 风格页表。 | ||
|
||
## 总结 | ||
|
||
目前的实现充分利用了 ArceOS 中已经存在的 axlibc 和页表,不足之处: | ||
|
||
- loader 对于 ArceOS 而言也是一个应用程序,但是其中跨层级调用了很多底层接口,不符合 ArceOS 的设计原则 | ||
- 不支持静态链接的 app,即程序中的系统调用指令 目前 ArceOS 无法处理 | ||
- 修改了一些 ArceOS 中的现有模块,使得原有模块的实现发生改变,如利用 `gp` 保存 `percpu` 变量,为了验证思路的可行性,将其改为了 `tp` |