多道程序放置与加载¶
多道程序放置¶
在第二章中,内核让所有应用都共享同一个固定的起始地址。 正因如此,内存中同时最多只能驻留一个应用,
要一次加载运行多个程序,就要求每个用户程序被内核加载到内存中的起始地址都不同。
为此,我们编写脚本 user/build.py
为每个应用定制各自的起始地址。
它的思路很简单,对于每一个应用程序,使用 cargo rustc
单独编译,
用 -Clink-args=-Ttext=xxxx
选项指定链接时 .text 段的地址为 0x80400000 + app_id * 0x20000
。
注解
qemu 预留的内存空间是有限的,如果加载的程序过多,程序地址超出内存空间,可能出现 core dumped
.
多道程序加载¶
在第二章中负责应用加载和执行的子模块 batch
被拆分为 loader
和 task
,
前者负责启动时加载应用程序,后者负责切换和调度。
其中, loader
模块的 load_apps
函数负责将所有用户程序在内核初始化的时一并加载进内存。
1 // os/src/loader.rs
2
3 pub fn load_apps() {
4 extern "C" {
5 fn _num_app();
6 }
7 let num_app_ptr = _num_app as usize as *const usize;
8 let num_app = get_num_app();
9 let app_start = unsafe { core::slice::from_raw_parts(num_app_ptr.add(1), num_app + 1) };
10 // clear i-cache first
11 unsafe {
12 core::arch::asm!("fence.i");
13 }
14 // load apps
15 for i in 0..num_app {
16 let base_i = get_base_i(i);
17 // clear region
18 (base_i..base_i + APP_SIZE_LIMIT)
19 .for_each(|addr| unsafe { (addr as *mut u8).write_volatile(0) });
20 // load app from data section to memory
21 let src = unsafe {
22 core::slice::from_raw_parts(app_start[i] as *const u8, app_start[i + 1] - app_start[i])
23 };
24 let dst = unsafe { core::slice::from_raw_parts_mut(base_i as *mut u8, src.len()) };
25 dst.copy_from_slice(src);
26 }
27 }
第 \(i\) 个应用被加载到以物理地址 base_i
开头的一段物理内存上,而 base_i
的计算方式如下:
1 // os/src/loader.rs
2
3 fn get_base_i(app_id: usize) -> usize {
4 APP_BASE_ADDRESS + app_id * APP_SIZE_LIMIT
5 }
我们可以在 config
子模块中找到这两个常数, APP_BASE_ADDRESS
被设置为 0x80400000
,
而 APP_SIZE_LIMIT
和上一章一样被设置为 0x20000
。这种放置方式与 user/build.py
的实现一致。