Skip to content

Commit 8f9e076

Browse files
committed
把 overview.md 讨论日志放到讨论记录
1 parent 6e83356 commit 8f9e076

File tree

3 files changed

+188
-188
lines changed

3 files changed

+188
-188
lines changed

content/design/overview.md

Lines changed: 0 additions & 188 deletions
Original file line numberDiff line numberDiff line change
@@ -651,194 +651,6 @@ Rust语法已有异步机制所需要的async和await;
651651

652652
TODO
653653

654-
655-
---
656-
657-
658-
# 日志
659-
660-
## **20210227-RISC-V进程地址空间切换交流**
661-
662-
### **吴一凡介绍目前的代码状态**
663-
664-
内核与用户进程各一个地址空间,共同有一个Trampoline(alltrap的代码)和TrapContext(alltrap的数据);Trampoline用于在用户地址空间和内核态时访问。
665-
666-
### **讨论**
667-
668-
#### **trampoline区域**
669-
670-
trampoline区域的初始化在哪?
671-
672-
1. trap_return, 链接时,把相关代码放到内核中;
673-
2. map_trampoline建立映射
674-
1. 用户态是不能访问的;
675-
2. 中断时,中断进入时硬件保存现场并不直接访问这个区域的内存,而是放在寄存器中;
676-
3. 这个区域只在内核态且是用户地址空间时访问;
677-
4. 这两个页表的设置是一样的,可以保证它只在内核态可以访问;
678-
5. 中断服务例程的地址在链接时得到;
679-
680-
这个思路的原始出处是哪?
681-
682-
XV6;MIT也没有维护X86的版本了。
683-
684-
#### **内核进程中的用户进程页表**
685-
686-
1. 每个进程在内核中有一个页表数据结构;
687-
2. 用户页表项:在内核中是线性映射;
688-
3. 每个用户进程的页表逻辑地址是不同的;
689-
#### **用户进程的内核栈**
690-
691-
1. 目前每个用户进程在内核进程中有一个自己的内核栈;
692-
2. 陈老师认为,每个用户进程必须有自己的内核栈,向勇认为可以没有;有兴趣时,同学可以来讨论。
693-
#### **系统调用时用户进程与内核进程间的切换过程**
694-
695-
有可能基于Trampoline完成整个切换过程吗?把内核中处理系统调用和调度的功能独立出来,形成两个或一个进程;这是有可能的;这个工作应该是hypervisor的功能;
696-
697-
#### **用户进程切换**
698-
699-
有可能进行进程间的切换吗?有可能;有时间时,再进入进一步的讨论;
700-
701-
#### **内核态的中断处理**
702-
703-
在内核态的中断的处理与用户态的有什么不同?没有地址空间切换了,服务一定是不一样的,可能还有其他区别;
704-
705-
### **建议**
706-
707-
1. 其他同学在看明白后,可以给出自己的理解和想法描述出来;
708-
2. 吴一凡:改[异步方案文档](https://shimo.im/docs/473QyY5re6tvXb3w?fileGuid=473QyY5re6tvXb3w)
709-
710-
# 20201111-交流
711-
712-
## 内核页表隔离
713-
714-
每个进程两个地址空间:用户和内核。用户地址空间只保留少部分内核空间数据,每次系统调用都从用户地址空间切到内核地址空间。防止熔断漏洞。
715-
716-
结论:目前暂时不这样实现
717-
718-
## 与 IPC 的统一
719-
720-
IO 核的运行可看做另一个进程,IO 核处理用户进程的 IO 请求相当于 IPC。这种设计可与传统的 IPC 统一,即使用 ring buffer,在两个用户进程之间进行 IPC。
721-
722-
# 20201101-交流
723-
724-
目前进展:基本完成普通核部分的内核功能,有简单的 C 用户程序和库。(step 3)
725-
726-
## 分工
727-
728-
jyk:完成 IO 核部分的内核功能:创建共享内存、调度 IO 协程、执行 IO。
729-
730-
zyr:制定系统调用接口和共享内存数据格式,用 C 编写用户态 IO 测试程序。对应上文提到的三种实现方式:3 异步 read、4b 同步写法的异步 read、4c 同步写法的异步 read。
731-
732-
lfy:编写 rust 用户库,支持运行 rust 用户程序,编写利用 rust async 创建多个协程的用户程序。然后利用 rust async 将 C 的 IO 测试程序改成 rust。对应上文提到的实现方式:4a 同步写法的异步 read。
733-
734-
# 20201014-讨论的实现方案
735-
736-
利用 io_uring/virtio 的思想,基于共享内存实现的高效异步系统调用接口。
737-
738-
假设系统有两个核,分别为:
739-
740-
1. 普通核:类似传统的 OS,有用户态和内核态,负责运行用户进程、调度用户进程、处理普通系统调用。
741-
2. IO 核:只有内核态,负责运行内核协程、调度内核协程、处理异步调用。实现了一套类似 io_uring 的机制,内核协程会不断 poll 共享内存中的请求队列,处理完毕后放入完成队列。使用 Rust async 实现。
742-
## 前期简化
743-
744-
有且仅有两个核;没有用户线程;单用户进程;没有抢占;
745-
746-
## 用户程序的实现
747-
748-
1. 通过系统调用,创建用户进程
749-
2. 通过系统调用,在 IO 核同时创建 io_uring 共享内存,和对应的内核协程(需要 IPI)。
750-
3. 异步 read:向请求队列写入 read 操作和所需的参数,立即返回。直到完成队列显示已完成,并得到返回结果。(可能需要通知机制?)
751-
4. 同步写法的异步 read(这部分是用户库的内容吗?是):
752-
1. 使用 Rust async 创建用户协程,poll 函数里检查对应的 IO 操作是否在完成队列中
753-
2. read 后轮询是否完成,用 yield 去别的进程执行一会儿
754-
3. read 后 wait 进入睡眠,直到完成时才被唤醒(通知机制?)
755-
## 请求队列与完成队列的设计
756-
757-
TODO
758-
759-
使用共享内存在用户进程与内核协程间共享。
760-
761-
是否要和 io_uring 完全一样?
762-
763-
是否还是两队列形式?更好的设计?
764-
765-
## 最小原型系统
766-
767-
预计实现的普通系统调用:(有用户/内核切换)
768-
769-
1. spawn:创建用户进程
770-
2. setup_io:创建共享内存和内核协程,返回共享内存地址
771-
3. yield:主动放弃当前进程的执行,进行进程调度
772-
4. *wait:主动放弃当前进程的执行,进行进程调度,直到 IO 操作完成时返回*
773-
774-
预计实现的异步调用:(只需写共享内存,无用户/内核切换)
775-
776-
1. open
777-
2. read
778-
3. write
779-
4. close
780-
781-
测试环境:
782-
783-
* RISCV64,QEMU/K210
784-
* memory fs/SD 卡
785-
## 实现步骤
786-
787-
方案一:从头开始造
788-
789-
1. RISCV64 QEMU 多核启动
790-
2. 普通核实现内核的基本功能(内存管理、进程管理、memory FS)
791-
3. 支持运行一个 hello_world 用户进程
792-
4. IO 核实现共享内存创建
793-
5. 内核协程创建与调度
794-
6. 实现 open/close/read/write
795-
7. 完善系统调用
796-
8. 编写性能测试程序,真机测试
797-
798-
方案二:将 rCore_Tutorial 改造成这种架构
799-
800-
## 其他问题
801-
802-
IO 核如何访问不同用户进程的IO数据?好像还是要切页表?
803-
804-
(第一版无需考虑)
805-
806-
完成时如何通知用户程序?
807-
808-
是否可在普通核开中断?
809-
810-
是否可以支持乱序完成?(似乎 io_uring 就是乱序的,可以通过参数强制顺序执行)
811-
812-
813-
---
814-
#
815-
816-
## 用户线程与地址空间相关的问题
817-
818-
#### 普通核
819-
820-
内核态有一个全局调度器,负责调度和执行用户线程(Thread)。每个线程都有独立的地址空间(可以多个线程共享同一地址空间),通过系统调用进入内核态时不发生地址空间切换,只有调度器进行线程切换时才进行地址空间切换。因此线程地址空间需要同时映射内核与用户的代码与数据。
821-
822-
*注:目前的实现线程==进程,是最基本的调度单位。以后可通过将共享地址空间的线程组成一个“线程组”,来表示进程的概念。在具体实现中,用户线程也被封装为了一个 rust future,通过 executor 进行调度。*
823-
824-
#### IO 核
825-
826-
只有一个地址空间,即内核地址空间,只映射内核的代码和数据,且不会在之后进行切换。也运行一个全局调度器,复制调度和执行用于 IO 的协程。当用户程序创建 IO 共享内存时,会同时在 IO 核的内核地址空间,和普通核对应的用户线程地址空间的用户数据段增加映射。
827-
828-
#### 关键问题
829-
830-
IO 核如何访问不同用户进程的IO buffer而不用频繁切页表?
831-
832-
目前想到的解决方法:(这几种方法可以一起用:共享内存放在特定区域,以优化地址转换;加一个描述缓冲区的数据结构,提高灵活性)
833-
834-
1. 创建专门的共享内存,存放 IO buffer。缺点:IO buffer 需要放在特定的地方,降低了灵活性,增加编程复杂性。
835-
2. 内核要访问用户数据时,手动进行用户虚拟地址到物理地址的转换,然后直接访问物理地址获取数据。缺点:降低安全性?对于不连续的页面每隔 4K 需要一次地址转换,开销大。适合 IO buffer 小到几个页面范围内的情况。
836-
3. 对方案 2 的优化:将 IO buffer 映射连续的物理地址,只需进行一次地址转换;使用大页,每隔 2M 进行一次地址转换;将用户虚拟地址到物理地址的映射结果缓存到专门的区域,以减少每次地址转换的开销(相当于4级页表的4次访存变为1次)。
837-
838-
---
839-
840-
841-
842654
---
843655

844656

content/discussion/2020.md

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
---
2+
title: 2020 年
3+
weight: 10
4+
bookToc: true
5+
---
6+
7+
# 20201111-交流
8+
9+
## 内核页表隔离
10+
11+
每个进程两个地址空间:用户和内核。用户地址空间只保留少部分内核空间数据,每次系统调用都从用户地址空间切到内核地址空间。防止熔断漏洞。
12+
13+
结论:目前暂时不这样实现
14+
15+
## 与 IPC 的统一
16+
17+
IO 核的运行可看做另一个进程,IO 核处理用户进程的 IO 请求相当于 IPC。这种设计可与传统的 IPC 统一,即使用 ring buffer,在两个用户进程之间进行 IPC。
18+
19+
# 20201101-交流
20+
21+
目前进展:基本完成普通核部分的内核功能,有简单的 C 用户程序和库。(step 3)
22+
23+
## 分工
24+
25+
jyk:完成 IO 核部分的内核功能:创建共享内存、调度 IO 协程、执行 IO。
26+
27+
zyr:制定系统调用接口和共享内存数据格式,用 C 编写用户态 IO 测试程序。对应上文提到的三种实现方式:3 异步 read、4b 同步写法的异步 read、4c 同步写法的异步 read。
28+
29+
lfy:编写 rust 用户库,支持运行 rust 用户程序,编写利用 rust async 创建多个协程的用户程序。然后利用 rust async 将 C 的 IO 测试程序改成 rust。对应上文提到的实现方式:4a 同步写法的异步 read。
30+
31+
# 20201014-讨论的实现方案
32+
33+
利用 io_uring/virtio 的思想,基于共享内存实现的高效异步系统调用接口。
34+
35+
假设系统有两个核,分别为:
36+
37+
1. 普通核:类似传统的 OS,有用户态和内核态,负责运行用户进程、调度用户进程、处理普通系统调用。
38+
2. IO 核:只有内核态,负责运行内核协程、调度内核协程、处理异步调用。实现了一套类似 io_uring 的机制,内核协程会不断 poll 共享内存中的请求队列,处理完毕后放入完成队列。使用 Rust async 实现。
39+
## 前期简化
40+
41+
有且仅有两个核;没有用户线程;单用户进程;没有抢占;
42+
43+
## 用户程序的实现
44+
45+
1. 通过系统调用,创建用户进程
46+
2. 通过系统调用,在 IO 核同时创建 io_uring 共享内存,和对应的内核协程(需要 IPI)。
47+
3. 异步 read:向请求队列写入 read 操作和所需的参数,立即返回。直到完成队列显示已完成,并得到返回结果。(可能需要通知机制?)
48+
4. 同步写法的异步 read(这部分是用户库的内容吗?是):
49+
1. 使用 Rust async 创建用户协程,poll 函数里检查对应的 IO 操作是否在完成队列中
50+
2. read 后轮询是否完成,用 yield 去别的进程执行一会儿
51+
3. read 后 wait 进入睡眠,直到完成时才被唤醒(通知机制?)
52+
## 请求队列与完成队列的设计
53+
54+
TODO
55+
56+
使用共享内存在用户进程与内核协程间共享。
57+
58+
是否要和 io_uring 完全一样?
59+
60+
是否还是两队列形式?更好的设计?
61+
62+
## 最小原型系统
63+
64+
预计实现的普通系统调用:(有用户/内核切换)
65+
66+
1. spawn:创建用户进程
67+
2. setup_io:创建共享内存和内核协程,返回共享内存地址
68+
3. yield:主动放弃当前进程的执行,进行进程调度
69+
4. *wait:主动放弃当前进程的执行,进行进程调度,直到 IO 操作完成时返回*
70+
71+
预计实现的异步调用:(只需写共享内存,无用户/内核切换)
72+
73+
1. open
74+
2. read
75+
3. write
76+
4. close
77+
78+
测试环境:
79+
80+
* RISCV64,QEMU/K210
81+
* memory fs/SD 卡
82+
## 实现步骤
83+
84+
方案一:从头开始造
85+
86+
1. RISCV64 QEMU 多核启动
87+
2. 普通核实现内核的基本功能(内存管理、进程管理、memory FS)
88+
3. 支持运行一个 hello_world 用户进程
89+
4. IO 核实现共享内存创建
90+
5. 内核协程创建与调度
91+
6. 实现 open/close/read/write
92+
7. 完善系统调用
93+
8. 编写性能测试程序,真机测试
94+
95+
方案二:将 rCore_Tutorial 改造成这种架构
96+
97+
## 其他问题
98+
99+
IO 核如何访问不同用户进程的IO数据?好像还是要切页表?
100+
101+
(第一版无需考虑)
102+
103+
完成时如何通知用户程序?
104+
105+
是否可在普通核开中断?
106+
107+
是否可以支持乱序完成?(似乎 io_uring 就是乱序的,可以通过参数强制顺序执行)
108+
109+
110+
---
111+
#
112+
113+
## 用户线程与地址空间相关的问题
114+
115+
#### 普通核
116+
117+
内核态有一个全局调度器,负责调度和执行用户线程(Thread)。每个线程都有独立的地址空间(可以多个线程共享同一地址空间),通过系统调用进入内核态时不发生地址空间切换,只有调度器进行线程切换时才进行地址空间切换。因此线程地址空间需要同时映射内核与用户的代码与数据。
118+
119+
*注:目前的实现线程==进程,是最基本的调度单位。以后可通过将共享地址空间的线程组成一个“线程组”,来表示进程的概念。在具体实现中,用户线程也被封装为了一个 rust future,通过 executor 进行调度。*
120+
121+
#### IO 核
122+
123+
只有一个地址空间,即内核地址空间,只映射内核的代码和数据,且不会在之后进行切换。也运行一个全局调度器,复制调度和执行用于 IO 的协程。当用户程序创建 IO 共享内存时,会同时在 IO 核的内核地址空间,和普通核对应的用户线程地址空间的用户数据段增加映射。
124+
125+
#### 关键问题
126+
127+
IO 核如何访问不同用户进程的IO buffer而不用频繁切页表?
128+
129+
目前想到的解决方法:(这几种方法可以一起用:共享内存放在特定区域,以优化地址转换;加一个描述缓冲区的数据结构,提高灵活性)
130+
131+
1. 创建专门的共享内存,存放 IO buffer。缺点:IO buffer 需要放在特定的地方,降低了灵活性,增加编程复杂性。
132+
2. 内核要访问用户数据时,手动进行用户虚拟地址到物理地址的转换,然后直接访问物理地址获取数据。缺点:降低安全性?对于不连续的页面每隔 4K 需要一次地址转换,开销大。适合 IO buffer 小到几个页面范围内的情况。
133+
3. 对方案 2 的优化:将 IO buffer 映射连续的物理地址,只需进行一次地址转换;使用大页,每隔 2M 进行一次地址转换;将用户虚拟地址到物理地址的映射结果缓存到专门的区域,以减少每次地址转换的开销(相当于4级页表的4次访存变为1次)。
134+

content/discussion/2021-02-27.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
---
2+
title: 2021 年 2 月
3+
bookToc: true
4+
---
5+
6+
## **20210227-RISC-V进程地址空间切换交流**
7+
8+
### **吴一凡介绍目前的代码状态**
9+
10+
内核与用户进程各一个地址空间,共同有一个Trampoline(alltrap的代码)和TrapContext(alltrap的数据);Trampoline用于在用户地址空间和内核态时访问。
11+
12+
### **讨论**
13+
14+
#### **trampoline区域**
15+
16+
trampoline区域的初始化在哪?
17+
18+
1. trap_return, 链接时,把相关代码放到内核中;
19+
2. map_trampoline建立映射
20+
1. 用户态是不能访问的;
21+
2. 中断时,中断进入时硬件保存现场并不直接访问这个区域的内存,而是放在寄存器中;
22+
3. 这个区域只在内核态且是用户地址空间时访问;
23+
4. 这两个页表的设置是一样的,可以保证它只在内核态可以访问;
24+
5. 中断服务例程的地址在链接时得到;
25+
26+
这个思路的原始出处是哪?
27+
28+
XV6;MIT也没有维护X86的版本了。
29+
30+
#### **内核进程中的用户进程页表**
31+
32+
1. 每个进程在内核中有一个页表数据结构;
33+
2. 用户页表项:在内核中是线性映射;
34+
3. 每个用户进程的页表逻辑地址是不同的;
35+
#### **用户进程的内核栈**
36+
37+
1. 目前每个用户进程在内核进程中有一个自己的内核栈;
38+
2. 陈老师认为,每个用户进程必须有自己的内核栈,向勇认为可以没有;有兴趣时,同学可以来讨论。
39+
#### **系统调用时用户进程与内核进程间的切换过程**
40+
41+
有可能基于Trampoline完成整个切换过程吗?把内核中处理系统调用和调度的功能独立出来,形成两个或一个进程;这是有可能的;这个工作应该是hypervisor的功能;
42+
43+
#### **用户进程切换**
44+
45+
有可能进行进程间的切换吗?有可能;有时间时,再进入进一步的讨论;
46+
47+
#### **内核态的中断处理**
48+
49+
在内核态的中断的处理与用户态的有什么不同?没有地址空间切换了,服务一定是不一样的,可能还有其他区别;
50+
51+
### **建议**
52+
53+
1. 其他同学在看明白后,可以给出自己的理解和想法描述出来;
54+
2. 吴一凡:改[异步方案文档](https://shimo.im/docs/473QyY5re6tvXb3w?fileGuid=473QyY5re6tvXb3w)

0 commit comments

Comments
 (0)