diff --git a/docs/_sources/chapter3/1multi-loader.rst.txt b/docs/_sources/chapter3/1multi-loader.rst.txt index 3415305c63db75965ed08bde21b61017e602c210..add2079721429ca07acfe824e7d19e132cb42db3 100644 --- a/docs/_sources/chapter3/1multi-loader.rst.txt +++ b/docs/_sources/chapter3/1multi-loader.rst.txt @@ -2,5 +2,118 @@ ===================================== 在本章的引言中我们提到每个应用都需要按照它的编号被分别加载到内存中不同的位置。本节我们就来介绍它是如何实现的。 -更具体来说, +与第二章相同,所有应用的 ELF 都经过 strip 丢掉所有 ELF header 和符号变为二进制镜像文件,随后以同样的格式通过 +``link_user.S`` 在编译的时候直接链接到内核的数据段中。不同的是,我们对相关模块进行了调整:在第二章中 +应用的加载和进度控制都交给 ``batch`` 子模块,而在第三章中我们将应用的加载这部分功能分离出来在 ``loader`` +子模块中实现,应用的执行和切换则交给 ``task`` 子模块。 + +应用的加载方式也和上一章不同。上一章的时候所有应用都被加载到一个固定的物理地址,也是因为这个原因,内存中同时 +最多只能驻留一个应用,当它运行完毕或者出错退出的时候由 ``batch`` 子模块加载一个新的应用来替换掉它。本章中, +所有的应用在内核初始化的时候就一并被加载到内存中。为了避免覆盖,它们自然需要被加载到不同的物理地址。这是通过 +调用 ``loader`` 子模块的 ``load_apps`` 函数实现的: + +.. code-block:: rust + :linenos: + + // os/src/loader.rs + + pub fn load_apps() { + extern "C" { fn _num_app(); } + let num_app_ptr = _num_app as usize as *const usize; + let num_app = get_num_app(); + let app_start = unsafe { + core::slice::from_raw_parts(num_app_ptr.add(1), num_app + 1) + }; + // clear i-cache first + unsafe { llvm_asm!("fence.i" :::: "volatile"); } + // load apps + for i in 0..num_app { + let base_i = get_base_i(i); + // clear region + (base_i..base_i + APP_SIZE_LIMIT).for_each(|addr| unsafe { + (addr as *mut u8).write_volatile(0) + }); + // load app from data section to memory + let src = unsafe { + core::slice::from_raw_parts( + app_start[i] as *const u8, + app_start[i + 1] - app_start[i] + ) + }; + let dst = unsafe { + core::slice::from_raw_parts_mut(base_i as *mut u8, src.len()) + }; + dst.copy_from_slice(src); + } + } + +可以看出,第 :math:`i` 个应用被加载到以物理地址 ``base_i`` 开头的一段物理内存上,而 ``base_i`` 的 +计算方式如下: + +.. code-block:: rust + :linenos: + + // os/src/loader.rs + + fn get_base_i(app_id: usize) -> usize { + APP_BASE_ADDRESS + app_id * APP_SIZE_LIMIT + } + +我们可以在 ``config`` 子模块中找到这两个常数。从这一章开始, ``config`` 子模块用来存放内核中所有的常数。看到 +``APP_BASE_ADDRESS`` 被设置为 ``0x80100000`` ,而 ``APP_SIZE_LIMIT`` 和上一章一样被设置为 +``0x20000`` ,也就是每个应用二进制镜像的大小限制。因此,应用的内存布局就很明朗了——就是从 +``APP_BASE_ADDRESS`` 开始依次为每个应用预留一段空间。 + +注意,我们需要调整每个应用被构建时候使用的链接脚本 ``linker.ld`` 中的起始地址 ``BASE_ADDRESS`` 为它实际 +会被内核加载并运行的地址。也就是要做到:应用知道自己会被加载到某个地址运行,而内核也确实能做到将它加载到那个 +地址。这算是应用和内核在某种意义上达成的一种协议。之所以要有这么苛刻的条件,是因为应用和内核的能力都很弱,泛用性很低。 +事实上,目前我们的应用是绝对位置而并不是位置无关的,内核也没有提供相应的重定位机制。 + +.. note:: + + 可以在 `这里 `_ 找到更多有关 + 位置无关和重定位的说明。 + +由于每个应用被加载到的位置都不同,也就导致它们 ``linker.ld`` 中的 ``BASE_ADDRESS`` 都是不同的。实际上, +我们写了一个脚本 ``build.py`` 而不是直接 ``cargo build`` 构建应用: + +.. code-block:: python + :linenos: + + # user/build.py + + import os + + base_address = 0x80100000 + step = 0x20000 + linker = 'src/linker.ld' + + app_id = 0 + apps = os.listdir('src/bin') + apps.sort() + for app in apps: + app = app[:app.find('.')] + lines = [] + lines_before = [] + with open(linker, 'r') as f: + for line in f.readlines(): + lines_before.append(line) + line = line.replace(hex(base_address), hex(base_address+step*app_id)) + lines.append(line) + with open(linker, 'w+') as f: + f.writelines(lines) + os.system('cargo build --bin %s --release' % app) + print('[build.py] application %s start with address %s' %(app, hex(base_address+step*app_id))) + with open(linker, 'w+') as f: + f.writelines(lines_before) + app_id = app_id + 1 + +它的思路很简单,在遍历 ``app`` 的大循环里面只做了这样几件事情: + +- 第 16~22 行,找到 ``src/linker.ld`` 中的 ``BASE_ADDRESS = 0x80100000;`` 这一行,并将后面的地址 + 替换为和当前应用对应的一个地址; +- 第 23 行,使用 ``cargo build`` 构建当前的应用,注意我们可以使用 ``--bin`` 参数来只构建某一个应用; +- 第 25~26 行,将 ``src/linker.ld`` 还原。 + +这样,我们就说明了多个应用是如何被构建和加载的。 diff --git a/docs/_sources/chapter3/index.rst.txt b/docs/_sources/chapter3/index.rst.txt index 0b2c2826890c4b2b9531d92a8c34087396afaa66..b8c375ebfd3de736d45407f5f1534a8055a1941c 100644 --- a/docs/_sources/chapter3/index.rst.txt +++ b/docs/_sources/chapter3/index.rst.txt @@ -1,4 +1,4 @@ -第三章:多道程序与分时多任务 +第三章:多道程序与分时多任务系统 ============================================== .. toctree:: @@ -20,6 +20,13 @@ 内存的不同区域中。由于目前我们只有一个 CPU,则同一时间最多只有一个应用在执行,剩下的应用则处于就绪状态,需要内核将 CPU 分配给它们才能 开始执行。因此,我们能够看到多个应用在一个 CPU 上交替执行的现象。 +.. note:: + + 读者也许会有疑问:由于只有一个 CPU,即使这样做,同一时间最多还是只能运行一个应用,还浪费了更多的内存来把所有 + 的应用都加载进来。那么这样做有什么意义呢? + + 读者可以带着这个问题继续看下去。后面我们会介绍这样做到底能够解决什么问题。 + .. _term-multiprogramming: .. _term-time-sharing-multitasking: diff --git a/docs/appendix-a/index.html b/docs/appendix-a/index.html index 646cddd4a12b9cfe313c8ee0d5e50783d165250f..e953b6842fcc05267bb1e8383b3a1a1d7044c7fa 100644 --- a/docs/appendix-a/index.html +++ b/docs/appendix-a/index.html @@ -123,7 +123,7 @@ commentsRunWhenDOMLoaded(addUtterances);
  • 环境配置
  • 第一章:RV64 裸机应用
  • 第二章:批处理系统
  • -
  • 第三章:多道程序与分时多任务
  • +
  • 第三章:多道程序与分时多任务系统
  • 第四章:内存隔离安全性
  • 第五章:进程及重要系统调用
  • 第六章:文件描述符与进程间通信
  • diff --git a/docs/appendix-b/index.html b/docs/appendix-b/index.html index e9fa56300b5afda22e21ffa7974fb1764d6a8068..b0af2f2c21ce2b671561c28df10b723a3fd878a3 100644 --- a/docs/appendix-b/index.html +++ b/docs/appendix-b/index.html @@ -123,7 +123,7 @@ commentsRunWhenDOMLoaded(addUtterances);
  • 环境配置
  • 第一章:RV64 裸机应用
  • 第二章:批处理系统
  • -
  • 第三章:多道程序与分时多任务
  • +
  • 第三章:多道程序与分时多任务系统
  • 第四章:内存隔离安全性
  • 第五章:进程及重要系统调用
  • 第六章:文件描述符与进程间通信
  • diff --git a/docs/appendix-c/index.html b/docs/appendix-c/index.html index 8102d5cf260512b6a35b11f506fc088136f08012..aa2508e8d07f54023b353d10fde0e2b79c2fc44f 100644 --- a/docs/appendix-c/index.html +++ b/docs/appendix-c/index.html @@ -123,7 +123,7 @@ commentsRunWhenDOMLoaded(addUtterances);
  • 环境配置
  • 第一章:RV64 裸机应用
  • 第二章:批处理系统
  • -
  • 第三章:多道程序与分时多任务
  • +
  • 第三章:多道程序与分时多任务系统
  • 第四章:内存隔离安全性
  • 第五章:进程及重要系统调用
  • 第六章:文件描述符与进程间通信
  • diff --git a/docs/chapter1/1app-ee-platform.html b/docs/chapter1/1app-ee-platform.html index 0c7b36fc048944845ab3bcb429d33b01df4d0333..a4e378f883dcd3b091726a18cb72196c794b9c98 100644 --- a/docs/chapter1/1app-ee-platform.html +++ b/docs/chapter1/1app-ee-platform.html @@ -136,7 +136,7 @@ commentsRunWhenDOMLoaded(addUtterances);
  • 第二章:批处理系统
  • -
  • 第三章:多道程序与分时多任务
  • +
  • 第三章:多道程序与分时多任务系统
  • 第四章:内存隔离安全性
  • 第五章:进程及重要系统调用
  • 第六章:文件描述符与进程间通信
  • diff --git a/docs/chapter1/2remove-std.html b/docs/chapter1/2remove-std.html index 604845135347aaafc61818f984a3cd2327d1a5ab..0530680f1297f64dde7346c674aa2975d53b69da 100644 --- a/docs/chapter1/2remove-std.html +++ b/docs/chapter1/2remove-std.html @@ -136,7 +136,7 @@ commentsRunWhenDOMLoaded(addUtterances);
  • 第二章:批处理系统
  • -
  • 第三章:多道程序与分时多任务
  • +
  • 第三章:多道程序与分时多任务系统
  • 第四章:内存隔离安全性
  • 第五章:进程及重要系统调用
  • 第六章:文件描述符与进程间通信
  • diff --git a/docs/chapter1/3minimal-rt.html b/docs/chapter1/3minimal-rt.html index db6770a2f906cb92541b842308406c6b527b2cbe..9bb133b99987e4a94b9e7edd22097f33a219a7bd 100644 --- a/docs/chapter1/3minimal-rt.html +++ b/docs/chapter1/3minimal-rt.html @@ -137,7 +137,7 @@ commentsRunWhenDOMLoaded(addUtterances);
  • 第二章:批处理系统
  • -
  • 第三章:多道程序与分时多任务
  • +
  • 第三章:多道程序与分时多任务系统
  • 第四章:内存隔离安全性
  • 第五章:进程及重要系统调用
  • 第六章:文件描述符与进程间通信
  • diff --git a/docs/chapter1/4load-manually.html b/docs/chapter1/4load-manually.html index 9012eede20b4f9278933888a362c945dcc469f3d..cdb6376dc5f3b00dc5e795a4897ec4eab7d45aae 100644 --- a/docs/chapter1/4load-manually.html +++ b/docs/chapter1/4load-manually.html @@ -136,7 +136,7 @@ commentsRunWhenDOMLoaded(addUtterances);
  • 第二章:批处理系统
  • -
  • 第三章:多道程序与分时多任务
  • +
  • 第三章:多道程序与分时多任务系统
  • 第四章:内存隔离安全性
  • 第五章:进程及重要系统调用
  • 第六章:文件描述符与进程间通信
  • diff --git a/docs/chapter1/5sbi-print.html b/docs/chapter1/5sbi-print.html index 8c4221f2485dc2787e365131e18594e158830738..1ac90c44d6a55acbddf84763d3dc39325c757ff5 100644 --- a/docs/chapter1/5sbi-print.html +++ b/docs/chapter1/5sbi-print.html @@ -133,7 +133,7 @@ commentsRunWhenDOMLoaded(addUtterances);
  • 第二章:批处理系统
  • -
  • 第三章:多道程序与分时多任务
  • +
  • 第三章:多道程序与分时多任务系统
  • 第四章:内存隔离安全性
  • 第五章:进程及重要系统调用
  • 第六章:文件描述符与进程间通信
  • diff --git a/docs/chapter1/6practice.html b/docs/chapter1/6practice.html index 2bc7ee31361f90bde3c721040cccf461d9aed411..b996caabcb4dbcae3ad14422c150325323764095 100644 --- a/docs/chapter1/6practice.html +++ b/docs/chapter1/6practice.html @@ -134,7 +134,7 @@ commentsRunWhenDOMLoaded(addUtterances);
  • 第二章:批处理系统
  • -
  • 第三章:多道程序与分时多任务
  • +
  • 第三章:多道程序与分时多任务系统
  • 第四章:内存隔离安全性
  • 第五章:进程及重要系统调用
  • 第六章:文件描述符与进程间通信
  • diff --git a/docs/chapter1/index.html b/docs/chapter1/index.html index 50d3854384cc187dbf7e0b1d1768bc37881682dc..c88dd3c88ff5cc12f38dcf5bd61372077ead408f 100644 --- a/docs/chapter1/index.html +++ b/docs/chapter1/index.html @@ -131,7 +131,7 @@ commentsRunWhenDOMLoaded(addUtterances);
  • 第二章:批处理系统
  • -
  • 第三章:多道程序与分时多任务
  • +
  • 第三章:多道程序与分时多任务系统
  • 第四章:内存隔离安全性
  • 第五章:进程及重要系统调用
  • 第六章:文件描述符与进程间通信
  • diff --git a/docs/chapter2/1rv-privilege.html b/docs/chapter2/1rv-privilege.html index ac2b888da4ece286d5467ed593d9d2d0ecd7195a..4b610965c2802517911f2b61cfd0f3e805135a63 100644 --- a/docs/chapter2/1rv-privilege.html +++ b/docs/chapter2/1rv-privilege.html @@ -131,7 +131,7 @@ commentsRunWhenDOMLoaded(addUtterances);
  • 处理 Trap
  • -
  • 第三章:多道程序与分时多任务
  • +
  • 第三章:多道程序与分时多任务系统
  • 第四章:内存隔离安全性
  • 第五章:进程及重要系统调用
  • 第六章:文件描述符与进程间通信
  • diff --git a/docs/chapter2/2application.html b/docs/chapter2/2application.html index f10f8b6588c8da237b08eca81181d01009794e91..128f9d7e9084097a8e53e2469be7150ffde85d91 100644 --- a/docs/chapter2/2application.html +++ b/docs/chapter2/2application.html @@ -135,7 +135,7 @@ commentsRunWhenDOMLoaded(addUtterances);
  • 处理 Trap
  • -
  • 第三章:多道程序与分时多任务
  • +
  • 第三章:多道程序与分时多任务系统
  • 第四章:内存隔离安全性
  • 第五章:进程及重要系统调用
  • 第六章:文件描述符与进程间通信
  • diff --git a/docs/chapter2/3batch-system.html b/docs/chapter2/3batch-system.html index 55be98b5c5b252a00fd809c9fc6a265113fb9aee..8cdf9905a45380e9ad847283ab6741159be1c00f 100644 --- a/docs/chapter2/3batch-system.html +++ b/docs/chapter2/3batch-system.html @@ -133,7 +133,7 @@ commentsRunWhenDOMLoaded(addUtterances);
  • 处理 Trap
  • -
  • 第三章:多道程序与分时多任务
  • +
  • 第三章:多道程序与分时多任务系统
  • 第四章:内存隔离安全性
  • 第五章:进程及重要系统调用
  • 第六章:文件描述符与进程间通信
  • diff --git a/docs/chapter2/4trap-handling.html b/docs/chapter2/4trap-handling.html index ac944a853e12d0522aec48d8e5de8d1578ac0875..a265c1c4118471ffec4bcabf47944f5d65e037a1 100644 --- a/docs/chapter2/4trap-handling.html +++ b/docs/chapter2/4trap-handling.html @@ -72,7 +72,7 @@ commentsRunWhenDOMLoaded(addUtterances); - + @@ -136,7 +136,7 @@ commentsRunWhenDOMLoaded(addUtterances); -
  • 第三章:多道程序与分时多任务
  • +
  • 第三章:多道程序与分时多任务系统
  • 第四章:内存隔离安全性
  • 第五章:进程及重要系统调用
  • 第六章:文件描述符与进程间通信
  • @@ -894,7 +894,7 @@ S 特权级,而它希望能够切换到 U 特权级。在 RISC-V 架构中, diff --git a/docs/chapter3/2task-switching.html b/docs/chapter3/2task-switching.html index 0ccb202470c91b1808ce27fca0cd5a2f0b36bf44..7fae7e623895f35548a16b0bf546403bbd13113c 100644 --- a/docs/chapter3/2task-switching.html +++ b/docs/chapter3/2task-switching.html @@ -123,7 +123,7 @@ commentsRunWhenDOMLoaded(addUtterances);
  • 环境配置
  • 第一章:RV64 裸机应用
  • 第二章:批处理系统
  • -
  • 第三章:多道程序与分时多任务