引言
====================

本章导读
--------------------

本章展现了操作系统一系列功能:

- 通过批处理支持多个程序的自动加载和运行
- 操作系统利用硬件特权级机制,实现对操作系统自身的保护

**批处理系统** (Batch System) 出现于计算资源匮乏的年代,其核心思想是:
将多个程序打包到一起输入计算机;当一个程序运行结束后,计算机会 *自动* 执行下一个程序。

应用程序难免会出错,如果一个程序的错误导致整个操作系统都无法运行,那就太糟糕了。*保护* 操作系统不受出错程序破坏的机制被称为 **特权级** (Privilege) 机制,它实现了用户态和内核态的隔离。

本章在上一章的基础上,让我们的 OS 内核能以批处理的形式一次运行多个应用程序,同时利用特权级机制,令 OS 不因出错的用户态程序而崩溃。将待执行的程序嵌入 OS 内核之中是十分粗暴的,也不符合我们对操作系统的认知。这同时也意味着我们将开始使用独立的测例文件,并把它们打包到 os 之中。

实践体验
--------------------

.. tabs::

    .. group-tab:: Rust

        获取本章代码:

        .. code-block:: console

            $ git clone https://github.com/LearningOS/rCore-Tutorial-2021Autumn.git
            $ cd rCore-Tutorial-2021Autumn
            $ git checkout ch2

        在 qemu 模拟器上运行本章代码:

        .. code-block:: console

            $ cd os
            $ make run LOG=INFO

        批处理系统自动加载并运行了所有的用户程序,尽管某些程序出错了:

        .. code-block:: 
        
            [rustsbi] RustSBI version 0.2.0-alpha.4
            .______       __    __      _______.___________.  _______..______   __
            |   _  \     |  |  |  |    /       |           | /       ||   _  \ |  |
            |  |_)  |    |  |  |  |   |   (----`---|  |----`|   (----`|  |_)  ||  |
            |      /     |  |  |  |    \   \       |  |      \   \    |   _  < |  |
            |  |\  \----.|  `--'  |.----)   |      |  |  .----)   |   |  |_)  ||  |
            | _| `._____| \______/ |_______/       |__|  |_______/    |______/ |__|

            [rustsbi] Implementation: RustSBI-QEMU Version 0.0.1
            [rustsbi-dtb] Hart count: cluster0 with 1 cores
            [rustsbi] misa: RV64ACDFIMSU
            [rustsbi] mideleg: ssoft, stimer, sext (0x222)
            [rustsbi] medeleg: ima, ia, bkpt, la, sa, uecall, ipage, lpage, spage (0xb1ab)
            [rustsbi] pmp0: 0x80000000 ..= 0x800fffff (rwx)
            [rustsbi] pmp1: 0x80000000 ..= 0x807fffff (rwx)
            [rustsbi] pmp2: 0x0 ..= 0xffffffffffffff (---)
            [rustsbi] enter supervisor 0x80200000
            [kernel] Hello, world!
            [ INFO] [kernel] num_app = 6
            [ INFO] [kernel] app_0 [0x8020b040, 0x8020f868)
            [ INFO] [kernel] app_1 [0x8020f868, 0x80214090)
            [ INFO] [kernel] app_2 [0x80214090, 0x80218988)
            [ INFO] [kernel] app_3 [0x80218988, 0x8021d160)
            [ INFO] [kernel] app_4 [0x8021d160, 0x80221a68)
            [ INFO] [kernel] app_5 [0x80221a68, 0x80226538)
            [ INFO] [kernel] Loading app_0
            [ERROR] [kernel] PageFault in application, core dumped.
            [ INFO] [kernel] Loading app_1
            [ERROR] [kernel] IllegalInstruction in application, core dumped.
            [ INFO] [kernel] Loading app_2
            [ERROR] [kernel] IllegalInstruction in application, core dumped.
            [ INFO] [kernel] Loading app_3
            [ INFO] [kernel] Application exited with code 1234
            [ INFO] [kernel] Loading app_4
            Hello, world from user mode program!
            [ INFO] [kernel] Application exited with code 0
            [ INFO] [kernel] Loading app_5
            3^10000=5079(MOD 10007)
            3^20000=8202(MOD 10007)
            3^30000=8824(MOD 10007)
            3^40000=5750(MOD 10007)
            3^50000=3824(MOD 10007)
            3^60000=8516(MOD 10007)
            3^70000=2510(MOD 10007)
            3^80000=9379(MOD 10007)
            3^90000=2621(MOD 10007)
            3^100000=2749(MOD 10007)
            Test power OK!
            [ INFO] [kernel] Application exited with code 0
            Panicked at src/batch.rs:68 All applications completed!

    .. group-tab:: C

        本章我们引入了用户程序,我们可以通过 ``make user`` 生成用户程序,最终将 ``.bin`` 文件放在 ``user/target/bin`` 目录下。

        .. code-block:: console

            $ git checkout ch2

        .. code-block:: console

            $ make user BASE=1 CHAPTER=2
            $ make run

        也可以直接运行打包好的测试程序。make test 会完成 make user 和 make run 两个步骤(自动设置 CHAPTER),我们可以通过 BASE 控制是否生成留做练习的测例。

        .. code-block:: console

            $ make test BASE=1

        如果你发现自己的 user 目录是空的,这是由于在 clone 的时候没有增加 ``--recursive`` 参数导致 submodule 没有初始化。解决方案如下:

        .. code-block:: console

            $ git submodule init 
            $ git submodule update

        如果顺利的话,我们可以看到批处理系统自动加载并运行所有的程序并且正确在程序出错的情况下保护了自身:

        .. code-block::

            .______       __    __      _______.___________.  _______..______   __
            |   _  \     |  |  |  |    /       |           | /       ||   _  \ |  |
            |  |_)  |    |  |  |  |   |   (----`---|  |----`|   (----`|  |_)  ||  |
            |      /     |  |  |  |    \   \       |  |      \   \    |   _  < |  |
            |  |\  \----.|  `--'  |.----)   |      |  |  .----)   |   |  |_)  ||  |
            | _| `._____| \______/ |_______/       |__|  |_______/    |______/ |__|

            [rustsbi] Platform: QEMU (Version 0.1.0)
            [rustsbi] misa: RV64ACDFIMSU
            [rustsbi] mideleg: 0x222
            [rustsbi] medeleg: 0xb1ab
            [rustsbi-dtb] Hart count: cluster0 with 1 cores
            [rustsbi] Kernel entry: 0x80200000
            hello wrold!
            Hello world from user mode program!
            Test hello_world OK!
            3^10000=5079
            3^20000=8202
            3^30000=8824
            3^40000=5750
            3^50000=3824
            3^60000=8516
            3^70000=2510
            3^80000=9379
            3^90000=2621
            3^100000=2749
            Test power OK!
            string from data section
            strinstring from stack section
            strin
            Test write1 OK!
            ALL DONE

        可以看到 4 个基础测试程序都可以正常运行。

本章代码树
--------------------

.. tabs::

    .. group-tab:: Rust

        .. code-block::

            .
            ├── ... (配置文件等)
            ├── os
            │  ├── build.rs (新增:生成 link_app.S 将应用作为一个数据段链接到内核)
            │  ├── Cargo.lock
            │  ├── Cargo.toml
            │  ├── Makefile (修改:构建内核之前先构建应用)
            │  └── src
            │     ├── batch.rs (新增:实现了一个简单的批处理系统)
            │     ├── console.rs
            │     ├── entry.asm
            │     ├── lang_items.rs
            │     ├── link_app.S (构建产物,由 os/build.rs 输出)
            │     ├── linker.ld
            │     ├── logging.rs
            │     ├── main.rs (修改:主函数中需要初始化 Trap 处理并加载和执行应用)
            │     ├── sbi.rs
            │     ├── sync (新增:包装了RefCell,暂时不用关心)
            │     │  ├── mod.rs
            │     │  └── up.rs
            │     ├── syscall (新增:系统调用子模块 syscall)
            │     │  ├── fs.rs(包含文件 I/O 相关的 syscall)
            │     │  ├── mod.rs(提供 syscall 方法根据 syscall ID 进行分发处理)
            │     │  └── process.rs(包含任务处理相关的 syscall)
            │     └── trap (新增:Trap 相关子模块 trap)
            │        ├── context.rs
            │        ├── mod.rs
            │        └── trap.S
            └── user (新增:应用测例保存在 user 目录下)
               ├── Cargo.lock
               ├── Cargo.toml
               ├── Makefile
               └── src
                  ├── bin (基于用户库 user_lib 开发的应用,每个应用放在一个源文件中)
                  │  └── ... 
                  ├── console.rs
                  ├── lang_items.rs
                  ├── lib.rs (用户库 user_lib)
                  ├── linker.ld (应用的链接脚本)
                  └── syscall.rs (包含 syscall 方法生成实际用于系统调用的汇编指令,
                                  各个具体的 syscall 都是通过 syscall 来实现的)

            ❯ tokei os
            ===============================================================================
            Language            Files        Lines         Code     Comments       Blanks
            ===============================================================================
            Assembly                1           12           11            0            1
            GNU Style Assembly      2           64           63            0            1
            Makefile                1           52           36            4           12
            TOML                    1           12            9            1            2
            -------------------------------------------------------------------------------
            Rust                   14          507          435           14           58
            |- Markdown             1           11            0            9            2
            (Total)                            518          435           23           60
            ===============================================================================
            Total                  19          647          554           19           74
            ===============================================================================

    .. group-tab:: C

        .. code-block::

            .
            ├── ... (配置文件等)
            ├── nfs
            │  └── fs
            ├── os
            │  ├── console.c
            │  ├── console.h
            │  ├── const.h
            │  ├── defs.h
            │  ├── entry.S
            │  ├── kernel.ld
            │  ├── kernelld.py
            │  ├── loader.c
            │  ├── loader.h
            │  ├── log.h
            │  ├── main.c
            │  ├── pack.py
            │  ├── printf.c
            │  ├── printf.h
            │  ├── riscv.h
            │  ├── sbi.c
            │  ├── sbi.h
            │  ├── string.c
            │  ├── string.h
            │  ├── syscall.c
            │  ├── syscall.h
            │  ├── syscall_ids.h
            │  ├── trampoline.S
            │  ├── trap.c
            │  ├── trap.h
            │  └── types.h
            ├── scripts
            │  ├── kernelld.py
            │  └── pack.py
            └── user

            ❯ tokei os
            ===============================================================================
            Language            Files        Lines         Code     Comments       Blanks
            ===============================================================================
            GNU Style Assembly      2          144          132            0           12
            C                       8          434          364           19           51
            C Header               13          906          759           39          108
            Python                  2          110          101            0            9
            ===============================================================================
            Total                  25         1594         1356           58          180
            ===============================================================================