0%

2024年开源操作系统训练营总结-winddevil/ArceOS第二节课笔记

学习内容

介绍地址空间和页表相关的内容.任务调度下次课说.

  1. 如何在一个主任务的基础上启用一个子任务,让他完成一系列的工作
  2. 启用一个单任务的基础上能够开两个任务,然后完成两个任务之间的通信

makefile的原理

调用是这样实现的:

首先是Makefile里的:

1
run: build justrun

它需要有buildjustrun这两个虚拟文件.

再去看build:

1
build: $(OUT_DIR) $(OUT_BIN)

需要的是OUT_DIROUT_BIN这两个实体文件.

创建它们两个的文件在scripts/make/build.mk:

1
2
3
4
5
$(OUT_DIR):
$(call run_cmd,mkdir,-p $@)

$(OUT_BIN): _cargo_build $(OUT_ELF)
$(call run_cmd,$(OBJCOPY),$(OUT_ELF) --strip-all -O binary $@)

这里调用的run_cmdscripts/make/utils.mk:

1
2
3
4
5
6
7
8
9
10
11
GREEN_C := \033[92;1m
CYAN_C := \033[96;1m
YELLOW_C := \033[93;1m
GRAY_C := \033[90m
WHITE_C := \033[37m
END_C := \033[0m

define run_cmd
@printf '$(WHITE_C)$(1)$(END_C) $(GRAY_C)$(2)$(END_C)\n'
@$(1) $(2)
endef

这里$(1)$(2)表示接受的是两个参数.

这个是两个操作,

  1. 通过颜色参数把要执行的命令输出出来(第一行)
  2. 第二行相当于执行接受的两个参数
  3. $@是代表这个虚拟文件本身

其实这一套操作下来就是创建这个OUT_DIR这个名字的文件夹.

Makefile中:

1
2
3
4
5
6
A ?= tour/u_1_0
APP ?= $(A)
... ...

# Paths
OUT_DIR ?= $(APP)

这时候把目光转回OUT_BIN.它在scripts/make/build.mk中:

1
2
$(OUT_BIN): _cargo_build $(OUT_ELF)
$(call run_cmd,$(OBJCOPY),$(OUT_ELF) --strip-all -O binary $@)

那么它的构建需要虚拟文件_cargo_build和实体文件OUT_ELF.

那么_cargo_build的功能也是先进行输出,随后调用cargo_build:

1
2
3
4
5
6
7
8
_cargo_build:
@printf " $(GREEN_C)Building$(END_C) App: $(APP_NAME), Arch: $(ARCH), Platform: $(PLATFORM_NAME), App type: $(APP_TYPE)\n"
ifeq ($(APP_TYPE), rust)
$(call cargo_build,$(APP),$(AX_FEAT) $(LIB_FEAT) $(APP_FEAT))
@cp $(rust_elf) $(OUT_ELF)
else ifeq ($(APP_TYPE), c)
$(call cargo_build,ulib/axlibc,$(AX_FEAT) $(LIB_FEAT))
endif

那么cargo_buildscripts/make/cargo.mk里:

1
2
3
define cargo_build
$(call run_cmd,cargo -C $(1) build,$(build_args) --features "$(strip $(2))")
endef

由于我们知道run_cmd是什么套路了,因此这边就是执行cargo来构建一个elf文件.

回到OUT_BIN这边,得到两个所需文件之后,通过OBJCOPY ?= rust-objcopy --binary-architecture=$(ARCH)(在Makefile)中定义,把elf多余的信息头去掉,只留下可执行二进制文件:

1
2
$(OUT_BIN): _cargo_build $(OUT_ELF)
$(call run_cmd,$(OBJCOPY),$(OUT_ELF) --strip-all -O binary $@)

最后回到justrun,它调用了run_qemu(在scripts/make/qemu.mk),

1
2
3
4
define run_qemu
@printf " $(CYAN_C)Running$(END_C) on qemu...\n"
$(call run_cmd,$(QEMU),$(qemu_args-y))
endef

这里调用了QEMU,是依赖于ARCH ?= riscv64(在Makefile):

1
QEMU := qemu-system-$(ARCH)

这里调用了qemu_args-y,同样是依赖于ARCH的这里不赘述:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
qemu_args-x86_64 := \
-machine q35 \
-kernel $(OUT_ELF)

qemu_args-riscv64 := \
-machine virt \
-bios default \
-kernel $(OUT_BIN)

qemu_args-aarch64 := \
-cpu cortex-a72 \
-machine virt \
-kernel $(OUT_BIN)

qemu_args-y := -m 128M -smp $(SMP) $(qemu_args-$(ARCH))

ReadPFlash

本节目标:

  1. 引入页表管理组件,通过地址空间重映射,支持设备MMIO
  2. 地址空间概念,重映射的意义,页表机制

希望能从PFlash把应用的数据加载进来,以为运行后边的程序做基础.

实验没有paging时的情况

正常运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
OpenSBI v0.9
____ _____ ____ _____
/ __ \ / ____| _ \_ _|
| | | |_ __ ___ _ __ | (___ | |_) || |
| | | | '_ \ / _ \ '_ \ \___ \| _ < | |
| |__| | |_) | __/ | | |____) | |_) || |_
\____/| .__/ \___|_| |_|_____/|____/_____|
| |
|_|

Platform Name : riscv-virtio,qemu
Platform Features : timer,mfdeleg
Platform HART Count : 1
Firmware Base : 0x80000000
Firmware Size : 100 KB
Runtime SBI Version : 0.2

Domain0 Name : root
Domain0 Boot HART : 0
Domain0 HARTs : 0*
Domain0 Region00 : 0x0000000080000000-0x000000008001ffff ()
Domain0 Region01 : 0x0000000000000000-0xffffffffffffffff (R,W,X)
Domain0 Next Address : 0x0000000080200000
Domain0 Next Arg1 : 0x0000000087000000
Domain0 Next Mode : S-mode
Domain0 SysReset : yes

Boot HART ID : 0
Boot HART Domain : root
Boot HART ISA : rv64imafdcsu
Boot HART Features : scounteren,mcounteren,time
Boot HART PMP Count : 16
Boot HART PMP Granularity : 4
Boot HART PMP Address Bits: 54
Boot HART MHPM Count : 0
Boot HART MHPM Count : 0
Boot HART MIDELEG : 0x0000000000000222
Boot HART MEDELEG : 0x000000000000b109

d8888 .d88888b. .d8888b.
d88888 d88P" "Y88b d88P Y88b
d88P888 888 888 Y88b.
d88P 888 888d888 .d8888b .d88b. 888 888 "Y888b.
d88P 888 888P" d88P" d8P Y8b 888 888 "Y88b.
d88P 888 888 888 88888888 888 888 "888
d8888888888 888 Y88b. Y8b. Y88b. .d88P Y88b d88P
d88P 888 888 "Y8888P "Y8888 "Y88888P" "Y8888P"

arch = riscv64
platform = riscv64-qemu-virt
target = riscv64gc-unknown-none-elf
smp = 1
build_mode = release
log_level = warn

Try to access dev region [0xFFFFFFC022000000], got 0x646C6670
Got pflash magic: pfld

pfld在哪?

scripts/make/utils.mk中,在pflash中写入了pfld:

1
2
3
4
5
6
7
8
9
10
define mk_pflash
@RUSTFLAGS="" cargo build -p origin --target riscv64gc-unknown-none-elf --release
@rust-objcopy --binary-architecture=riscv64 --strip-all -O binary ./target/riscv64gc-unknown-none-elf/release/origin /tmp/origin.bin
@printf "pfld\00\00\00\01" > /tmp/prefix.bin
@printf "%08x" `stat -c "%s" /tmp/origin.bin` | xxd -r -ps > /tmp/size.bin
@cat /tmp/prefix.bin /tmp/size.bin > /tmp/head.bin
@dd if=/dev/zero of=./$(1) bs=1M count=32
@dd if=/tmp/head.bin of=./$(1) conv=notrunc
@dd if=/tmp/origin.bin of=./$(1) seek=16 obs=1 conv=notrunc
endef

那么这个在哪里调用呢?答案是没有调用.

我们是直接pull下来的,如果调用make pflash_img就会重新生成它.

scripts/make/qemu.mk里:

1
2
3
4
5
6
7
8
9
10
qemu_args-y := -m 128M -smp $(SMP) $(qemu_args-$(ARCH))

qemu_args-$(PFLASH) += \
-drive if=pflash,file=$(CURDIR)/$(PFLASH_IMG),format=raw,unit=1
... ...

define run_qemu
@printf " $(CYAN_C)Running$(END_C) on qemu...\n"
$(call run_cmd,$(QEMU),$(qemu_args-y))
endef

而在MakefilePFLASH被指定为y.这样就会在运行qemu的时候加上pflash.img这个文件.

没有指定paging的情况

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#![cfg_attr(feature = "axstd", no_std)]
#![cfg_attr(feature = "axstd", no_main)]

#[macro_use]
#[cfg(feature = "axstd")]
extern crate axstd as std;

use core::{mem, str};
use std::os::arceos::modules::axhal::mem::phys_to_virt;

/// Physical address for pflash#1
const PFLASH_START: usize = 0x2200_0000;

#[cfg_attr(feature = "axstd", no_mangle)]
fn main() {
// Makesure that we can access pflash region.
let va = phys_to_virt(PFLASH_START.into()).as_usize();
let ptr = va as *const u32;
unsafe {
println!("Try to access dev region [{:#X}], got {:#X}", va, *ptr);
let magic = mem::transmute::<u32, [u8; 4]>(*ptr);
println!("Got pflash magic: {}", str::from_utf8(&magic).unwrap());
}
}

这里需要看下一节关于PFlash的部分,因为这时候需要访问外设,在没有remap的时候是1G恒等映射,外设没有映射到地址空间中,因此报错.

一开始只映射了物理空间的0x8000_00000xC000_0000.

这里访问的物理地址是0x2200_0000,本来就不属于刚刚提到的物理空间,因此通过恒等映射平移也得不到恒等映射之后的虚拟地址.

产生的log:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
OpenSBI v0.9
____ _____ ____ _____
/ __ \ / ____| _ \_ _|
| | | |_ __ ___ _ __ | (___ | |_) || |
| | | | '_ \ / _ \ '_ \ \___ \| _ < | |
| |__| | |_) | __/ | | |____) | |_) || |_
\____/| .__/ \___|_| |_|_____/|____/_____|
| |
|_|

Platform Name : riscv-virtio,qemu
Platform Features : timer,mfdeleg
Platform HART Count : 1
Firmware Base : 0x80000000
Firmware Size : 100 KB
Runtime SBI Version : 0.2

Domain0 Name : root
Domain0 Boot HART : 0
Domain0 HARTs : 0*
Domain0 Region00 : 0x0000000080000000-0x000000008001ffff ()
Domain0 Region01 : 0x0000000000000000-0xffffffffffffffff (R,W,X)
Domain0 Next Address : 0x0000000080200000
Domain0 Next Arg1 : 0x0000000087000000
Domain0 Next Mode : S-mode
Domain0 SysReset : yes

Boot HART ID : 0
Boot HART Domain : root
Boot HART ISA : rv64imafdcsu
Boot HART Features : scounteren,mcounteren,time
Boot HART PMP Count : 16
Boot HART PMP Granularity : 4
Boot HART PMP Address Bits: 54
Boot HART MHPM Count : 0
Boot HART MHPM Count : 0
Boot HART MIDELEG : 0x0000000000000222
Boot HART MEDELEG : 0x000000000000b109

d8888 .d88888b. .d8888b.
d88888 d88P" "Y88b d88P Y88b
d88P888 888 888 Y88b.
d88P 888 888d888 .d8888b .d88b. 888 888 "Y888b.
d88P 888 888P" d88P" d8P Y8b 888 888 "Y88b.
d88P 888 888 888 88888888 888 888 "888
d8888888888 888 Y88b. Y8b. Y88b. .d88P Y88b d88P
d88P 888 888 "Y8888P "Y8888 "Y88888P" "Y8888P"

arch = riscv64
platform = riscv64-qemu-virt
target = riscv64gc-unknown-none-elf
smp = 1
build_mode = release
log_level = warn

Try to access dev region [0xFFFFFFC022000000], got [ 2.002842 0 axruntime::lang_items:5] panicked at modules/axhal/src/arch/riscv/trap.rs:55:13:
Unhandled trap Exception(LoadFault) @ 0xffffffc080203fa6:
TrapFrame {
regs: GeneralRegisters {
ra: 0xffffffc08020376c,
sp: 0xffffffc0802499f0,
gp: 0x0,
tp: 0x0,
t0: 0x3f,
t1: 0x23,
t2: 0x5,
s0: 0xffffffc080249a80,
s1: 0xffffffc080249c60,
a0: 0xffffffc022000000,
a1: 0xffffffc080249a80,
a2: 0x0,
a3: 0xffffffc080203f9e,
a4: 0x2,
a5: 0xffffffc080202b76,
a6: 0xa,
a7: 0x1,
s2: 0x2,
s3: 0xffffffc080249bc0,
s4: 0xffffffc080249bf0,
s5: 0x38,
s6: 0xffffffc080205098,
s7: 0x2,
s8: 0x1,
s9: 0x24000,
s10: 0x2,
s11: 0xffffffc08026e000,
t3: 0x23,
t4: 0x3a,
t5: 0x5000,
t6: 0x55555555,
},
sepc: 0xffffffc080203fa6,
sstatus: 0x8000000000006100,
}

分支名称:tour_u_3_0_no_paging

MAP和REMAP

ArceOS Unikernel包括两阶段地址空间映射,
Boot阶段默认开启1G空间的恒等映射;
如果需要支持设备MMIO区间,通过指定一个feature - “paging”来实现重映射。

上一节说了启动之后需要remap,这样才可以实现重映射.

那么就需要打开paging.

初始化的线性页表

其实是创建了两个映射,相当于拿前一个恒等映射做了跳板,因为要求开启MMU之后仍然可以以原来的物理地址正常访问.

#TODO

后续创建多级页表

#TODO

PFlash

是一个模拟闪存磁盘.QEMU启动的时候会自动从内存中加载内容到固定的MMIO区域.

读操作是不需要驱动的,但是写是需要驱动的.

目前我们只需要读,只要加载成功即可.

物理地址空间

外设被映射到一个物理地址空间里边.

注意:linker_riscv64-qemu-virt.lds,段布局都是听的它的.

分页

类似于rCore的三级页表,我们实验中用的也是SV39.

分页阶段1-恒等映射+偏移

我们希望sbikernel都保存在高地址空间.

  1. 开局的时候把虚拟空间内的地址和物理空间内的地址完全对应上.
  2. 给指针寄存器pc栈寄存器sp加偏移,这里的偏移是0xffff_ffc0_0000_0000.

sbi还是放在0x8000_0000,kernel还是放在0x8020_0000.

那么在这个情况下其实已经是物理地址了,就是一个线性偏移的操作实现虚拟地址和物理地址的映射.

如果不需要访问物理设备,现在就可以完成了.

分页阶段2-重建映射

重映射的时候干脆在虚拟地址空间里把sbi去掉,因为不应该继续访问sbi了.

不同的数据段的权限不一样,比如READ,WRITE,EXECUTE不一样.比如代码段就只能读和运行不能写.在重建的时候就不需要给它这些权限.

这样设备的地址空间也可以被映射进来,权限粒度也更细.

这里似乎仍然是线性映射.

多任务

需求:启用多任务,开一个子任务,让子任务替主任务完成一些具体工作,然后回到主任务.

并发是多个任务同时在等待使用CPU而不是同时运行.并行是真的需要同时运行.

调度的一个很好的描述:一个是保存现场,一个是任务无感知.

实验多任务

make run A=tour/u_4_0

任务代码解析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#![cfg_attr(feature = "axstd", no_std)]
#![cfg_attr(feature = "axstd", no_main)]

#[macro_use]
#[cfg(feature = "axstd")]
extern crate axstd as std;

use core::{mem, str};
use std::thread;
use std::os::arceos::modules::axhal::mem::phys_to_virt;

/// Physical address for pflash#1
const PFLASH_START: usize = 0x2200_0000;

#[cfg_attr(feature = "axstd", no_mangle)]
fn main() {
println!("Multi-task is starting ...");

let worker = thread::spawn(move || {
println!("Spawned-thread ...");

// Makesure that we can access pflash region.
let va = phys_to_virt(PFLASH_START.into()).as_usize();
let ptr = va as *const u32;
let magic = unsafe {
mem::transmute::<u32, [u8; 4]>(*ptr)
};
if let Ok(s) = str::from_utf8(&magic) {
println!("Got pflash magic: {s}");
0
} else {
-1
}
});

let ret = worker.join();
// Makesure that worker has finished its work.
assert_eq!(ret, Ok(0));

println!("Multi-task OK!");
}

和上一节的内容一样,同样是访问了我们预导入的pflash.img的前几个字符pfld.

只不过用了spawn的方法生成,并且用join的方法等待.

任务的数据结构

有一个关键点在于task_ext,是任务的拓展属性,是面向宏内核Hypervisor的关键.

通用调度框架

分层,并且实现同样的接口,这样就可以自己决定是什么样的调度机制.

系统默认内置任务

  • GC: 除main之外的任务(线程)退出后,由gc负责回收清理。
  • IDLE: 当其它所有任务都阻塞时,执行它。对某些arch,wait_for_irqs对应非忙等指令.比如等待中断什么的,而不是忙等,如果发生新的”event“(自己取的名)那么就会马上响应.

MsgQueue

本节目标:

  1. 任务的切换机制,协作式调度算法
  2. 同步的方式,Mutex的机制

运行实验

执行make run A=tour/u_5_0:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
OpenSBI v0.9
____ _____ ____ _____
/ __ \ / ____| _ \_ _|
| | | |_ __ ___ _ __ | (___ | |_) || |
| | | | '_ \ / _ \ '_ \ \___ \| _ < | |
| |__| | |_) | __/ | | |____) | |_) || |_
\____/| .__/ \___|_| |_|_____/|____/_____|
| |
|_|

Platform Name : riscv-virtio,qemu
Platform Features : timer,mfdeleg
Platform HART Count : 1
Firmware Base : 0x80000000
Firmware Size : 100 KB
Runtime SBI Version : 0.2

Domain0 Name : root
Domain0 Boot HART : 0
Domain0 HARTs : 0*
Domain0 Region00 : 0x0000000080000000-0x000000008001ffff ()
Domain0 Region01 : 0x0000000000000000-0xffffffffffffffff (R,W,X)
Domain0 Next Address : 0x0000000080200000
Domain0 Next Arg1 : 0x0000000087000000
Domain0 Next Mode : S-mode
Domain0 SysReset : yes

Boot HART ID : 0
Boot HART Domain : root
Boot HART ISA : rv64imafdcsu
Boot HART Features : scounteren,mcounteren,time
Boot HART PMP Count : 16
Boot HART PMP Granularity : 4
Boot HART PMP Address Bits: 54
Boot HART MHPM Count : 0
Boot HART MHPM Count : 0
Boot HART MIDELEG : 0x0000000000000222
Boot HART MEDELEG : 0x000000000000b109

d8888 .d88888b. .d8888b.
d88888 d88P" "Y88b d88P Y88b
d88P888 888 888 Y88b.
d88P 888 888d888 .d8888b .d88b. 888 888 "Y888b.
d88P 888 888P" d88P" d8P Y8b 888 888 "Y88b.
d88P 888 888 888 88888888 888 888 "888
d8888888888 888 Y88b. Y8b. Y88b. .d88P Y88b d88P
d88P 888 888 "Y8888P "Y8888 "Y88888P" "Y8888P"

arch = riscv64
platform = riscv64-qemu-virt
target = riscv64gc-unknown-none-elf
smp = 1
build_mode = release
log_level = warn

Multi-task is starting ...
Wait for workers to exit ...
worker1 ...
worker1 [0]
worker2 ...
worker2 [0]
worker2: nothing to do!
worker1 [1]
worker2 [1]
worker2: nothing to do!
worker1 [2]
worker2 [2]
worker2: nothing to do!
worker1 [3]
worker2 [3]
worker2: nothing to do!
worker1 [4]
worker2 [4]
worker2: nothing to do!
worker1 [5]
worker2 [5]
worker2: nothing to do!
worker1 [6]
worker2 [6]
worker2: nothing to do!
worker1 [7]
worker2 [7]
worker2: nothing to do!
worker1 [8]
worker2 [8]
worker2: nothing to do!
worker1 [9]
worker2 [9]
worker2: nothing to do!
worker1 [10]
worker2 [10]
worker2: nothing to do!
worker1 [11]
worker2 [11]
worker2: nothing to do!
worker1 [12]
worker2 [12]
worker2: nothing to do!
worker1 [13]
worker2 [13]
worker2: nothing to do!
worker1 [14]
worker2 [14]
worker2: nothing to do!
worker1 [15]
worker2 [15]
worker2: nothing to do!
worker1 [16]
worker2 [16]
worker2: nothing to do!
worker1 [17]
worker2 [17]
worker2: nothing to do!
worker1 [18]
worker2 [18]
worker2: nothing to do!
worker1 [19]
worker2 [19]
worker2: nothing to do!
worker1 [20]
worker2 [20]
worker2: nothing to do!
worker1 [21]
worker2 [21]
worker2: nothing to do!
worker1 [22]
worker2 [22]
worker2: nothing to do!
worker1 [23]
worker2 [23]
worker2: nothing to do!
worker1 [24]
worker2 [24]
worker2: nothing to do!
worker1 [25]
worker2 [25]
worker2: nothing to do!
worker1 [26]
worker2 [26]
worker2: nothing to do!
worker1 [27]
worker2 [27]
worker2: nothing to do!
worker1 [28]
worker2 [28]
worker2: nothing to do!
worker1 [29]
worker2 [29]
worker2: nothing to do!
worker1 [30]
worker2 [30]
worker2: nothing to do!
worker1 [31]
worker2 [31]
worker2: nothing to do!
worker1 [32]
worker2 [32]
worker2: nothing to do!
worker1 [33]
worker2 [33]
worker2: nothing to do!
worker1 [34]
worker2 [34]
worker2: nothing to do!
worker1 [35]
worker2 [35]
worker2: nothing to do!
worker1 [36]
worker2 [36]
worker2: nothing to do!
worker1 [37]
worker2 [37]
worker2: nothing to do!
worker1 [38]
worker2 [38]
worker2: nothing to do!
worker1 [39]
worker2 [39]
worker2: nothing to do!
worker1 [40]
worker2 [40]
worker2: nothing to do!
worker1 [41]
worker2 [41]
worker2: nothing to do!
worker1 [42]
worker2 [42]
worker2: nothing to do!
worker1 [43]
worker2 [43]
worker2: nothing to do!
worker1 [44]
worker2 [44]
worker2: nothing to do!
worker1 [45]
worker2 [45]
worker2: nothing to do!
worker1 [46]
worker2 [46]
worker2: nothing to do!
worker1 [47]
worker2 [47]
worker2: nothing to do!
worker1 [48]
worker2 [48]
worker2: nothing to do!
worker1 [49]
worker2 [49]
worker2: nothing to do!
worker1 [50]
worker2 [50]
worker2: nothing to do!
worker1 [51]
worker2 [51]
worker2: nothing to do!
worker1 [52]
worker2 [52]
worker2: nothing to do!
worker1 [53]
worker2 [53]
worker2: nothing to do!
worker1 [54]
worker2 [54]
worker2: nothing to do!
worker1 [55]
worker2 [55]
worker2: nothing to do!
worker1 [56]
worker2 [56]
worker2: nothing to do!
worker1 [57]
worker2 [57]
worker2: nothing to do!
worker1 [58]
worker2 [58]
worker2: nothing to do!
worker1 [59]
worker2 [59]
worker2: nothing to do!
worker1 [60]
worker2 [60]
worker2: nothing to do!
worker1 [61]
worker2 [61]
worker2: nothing to do!
worker1 [62]
worker2 [62]
worker2: nothing to do!
worker1 [63]
worker2 [63]
worker2: nothing to do!
worker1 [64]
worker2 [64]
worker2 ok!
worker1 ok!
Multi-task OK!

分析代码,worker1是尝试获取Arc中的这个双端队列,然后尝试在队列的最后放东西.

由于是协作式调度,worker1每次放入的之后都会yield,因此worker2就会接手,然后尝试把队列里所有的内容都打出来,如果队列为空就报告worker2: nothing to do!,然后再由worker1接手CPU.

协作式调度

rCore不同,现在使用的是List而不是一个数组,原理上就不设置任务的个数了.

互斥锁和自旋锁

自旋锁可能是我们脑子中的那个锁,每次访问资源需要访问这个锁,如果没办法访问那你这个任务要处理这种情况.

互斥锁则是自己加了一个等待队列,如果有任务在等待这个资源,那么这个任务被加入等待队列之后不会参与调度,这样就节省了很多任务切换时的资源.

bump内存分配算法

#TODO

挑战性作业

initialize global allocator at: [0xffffffc08026f000, 0xffffffc088000000)

5376*2

align是8的是有关于Vec的内存的分配

131,665,920

1048576

524288+8000

#TODO