首先,.section .data 表示定义了一个数据段,在这个段中定义了一系列的全局变量。其中,_app_num 是一个标签,表示一个 64 位的整数,初始值为 23。接下来是一系列的标签,分别代表了应用程序的起始地址,每个标签都是 64 位的整数。

接着,.section .data 后面又出现了一个标签 _app_names,它是一个字符串数组,包含了一组字符串,分别命名为 “ch2b_exit”、“ch2b_hello_world”、“ch2b_power” 等等。这些字符串名字对应了前面定义的应用程序的起始地址。

再往下,出现了一个标签 INIT_PROC,它是一个字符串,表示初始化进程的名称,值为 “usershell”。

之后,每个应用程序都有自己的标签和段名,比如 app_0_startapp_1_start 等等。每个标签都包含一个指令 .incbin,它用于将一个二进制文件(以字符串形式指定文件路径)插入到当前段中。

进程初始化分析

scheduler()
    fetch_task() // 获取下一个要执行的进程
    swtch(&curenv->context, nextenv->context) // 切换到下一个进程上下文
// Per-process state
struct proc {
    enum procstate    state;     // 进程状态
    int               pid;       // 进程 ID
    uint64            ustack;    // 进程用户栈虚拟地址 (用户页表)
    uint64            kstack;    // 进程内核栈虚拟地址 (内核页表)
    struct trapframe *trapframe; // 进程中断帧
    struct context    context; // 用于保存进程内核态的寄存器信息,进程切换时使用
    pagetable_t       pagetable; // User page table
    uint64            max_page;
    uint64            program_brk;
    uint64            heap_bottom;
    struct proc *     parent; // Parent process
    uint64            exit_code;
    struct file *     files[FD_BUFFER_SIZE];
    uint32     syscall_times[MAX_SYSCALL_NUM]; // 系统调用次数统计
    uint64     start_time;                     // 进程开始运行时间
    struct vma vmas[NVMA];                     // 虚拟内存区域
};

wait 系统调用的功能

wait 系统调用是用于处理子进程终止状态的系统调用。其主要功能是等待子进程的终止,并获取子进程的退出状态信息。在操作系统中,当一个父进程创建了一个子进程后,通常会使用 wait 来等待子进程的终止,以便进行后续的处理,如回收子进程的资源或获取其运行结果。

以下是 wait 系统调用的主要功能:

  1. 等待子进程终止:父进程调用 wait 系统调用后,会进入阻塞状态,等待子进程终止。如果子进程已经终止,那么 wait 立即返回,否则父进程会一直等待直到子进程终止。

  2. 获取子进程的退出状态wait 系统调用会获取子进程的退出状态信息,包括子进程的退出码(通常是一个整数)。这个退出码可以告诉父进程子进程的终止情况,例如是否成功执行等。

  3. 回收子进程资源:一旦子进程终止,其占用的系统资源(如内存、文件描述符等)通常需要由父进程来回收,以避免资源泄漏。wait 系统调用在等待子进程终止后,会自动回收这些资源。

  4. 处理僵尸进程:在某些情况下,子进程可能会在终止后成为僵尸进程,即已经终止但其进程描述符仍然存在。父进程可以使用 wait 来回收这些僵尸进程,释放相关资源。

execforkspawn 是操作系统中常见的进程管理系统调用,各自具有不同的功能和用途:

  1. exec 系统调用

    • 功能exec 系统调用用于在当前进程的上下文中加载并执行一个新的程序。
    • 用途:通常在一个进程需要替换自身的执行映像时使用。它会加载一个新的可执行文件,覆盖当前进程的地址空间和代码段,然后开始执行新的程序。这个新程序可以是完全不同的程序,从而允许进程动态切换到不同的应用程序,而不需要创建新的进程。
  2. fork 系统调用

    • 功能fork 系统调用用于创建一个与当前进程几乎完全相同的新进程,包括代码、数据和上下文等。
    • 用途:通常用于创建新的进程,新进程称为子进程,它从父进程继承了大部分状态,然后可以在独立的地址空间中执行不同的操作。fork 创建的子进程是父进程的副本,可以并行执行不同的任务。
  3. spawn 系统调用

    • 功能spawn 系统调用通常用于创建新的进程并执行指定的程序。
    • 用途:类似于 fork,它也创建了一个新的进程,但不像 fork 那样完全复制父进程。相反,spawn 允许你指定一个新程序的路径和参数,而不是完全复制当前进程的状态。这使得它更适合用于启动新程序,而不是简单地创建一个进程副本。

总结:

  • exec 用于替换当前进程的执行映像,允许加载和执行新程序。
  • fork 用于创建一个几乎与父进程相同的新进程,新进程成为父进程的副本。
  • spawn 通常用于创建一个新进程并执行指定的程序,允许指定不同的程序路径和参数。

附录

本章任务: 在次 -> 在此