应用程序的一次执行过程称为一个任务。

在 BTD-OS 中我们将进程和线程统一用任务控制块 (TaskContralBlock) 结构表示,维护着操作系统对于任务的管理信息,结构如下:

// branch-main: kernel/src/task/task.rs
pub struct TaskControlBlock {
    // immutable
    pub pid: PidHandle,
    pub tgid: usize,
    pub kernel_stack: KernelStack,

    // mutable according to clone flags
    pub sigactions: Arc<RwLock<[SigAction; MAX_SIGNUM as usize]>>,
    pub memory_set: Arc<RwLock<MemorySet>>,
    pub fd_table: Arc<RwLock<FDTable>>,

    // mutable
    inner: RwLock<TaskControlBlockInner>,
}
  • pid / tgid 由全局分配器 PID_ALLOCATOR 分配,并且分配的 ID 仅增长不回收,因为 BTD-OS 实现线程的过程中,默认认为 tgid 不小于 pid;
  • 若该任务控制块为进程,则 TaskControlBlock 的 pid 与 tgid 字段值相同;
  • pid 与 tgid 的差值可以用于计算线程 TrapContext 虚拟地址;
  • kernel_stack 字段仅用于创建时分配内核栈,故无需修改;
  • sigactions, memory_set, fd_table 字段均需要根据 fork 时的 CloneFlags 参数结合 man-page 要求创建;
pub struct TaskControlBlockInner {
    pub trap_cx_ppn: PhysPageNum,
    pub task_cx: TaskContext,
    pub task_status: TaskStatus,
    pub trap_cause: Option<Scause>,

    pub parent: Option<Weak<TaskControlBlock>>,
    // child process and thread collection
    pub children: Vec<Arc<TaskControlBlock>>,

    pub pending_signals: SigSet,
    pub sigmask: SigMask,

    pub cwd: AbsolutePath,
    pub exit_code: i32,

    pub interval_timer: Option<IntervalTimer>,
    pub utime: TimeVal,
    pub stime: TimeVal,
    pub last_enter_umode_time: TimeVal,
    pub last_enter_smode_time: TimeVal,

    pub robust_list: RobustList,
    pub rlimit_nofile: RLimit,

    pub clear_child_tid: usize, /* CLONE_CHILD_CLEARTID */
}
  • trap_cause 用于记录 TCB 进入 trap_handler 的原因,BTD-OS 具体用于时钟中断更新时间片的问题(后文会介绍 BTD-OS 为何改进时钟中断处理);
  • parent 表示可能存在的父进程;
  • children 用于收集 fork 时创建的子进程/子线程的 Arc (原子引用计数);
  • pending_signals 表示待处理的信号集;
  • sigmask 表示被屏蔽的信号集;
  • interval_timer 用于处理与定时器相关的系统调用;
  • utime, stime, last_enter_umode_time, last_enter_smode_time 用于记录 TCB 分别在 U Mode 和 S Mode 下所耗费的时间,用于处理 getrusage 系统调用;
  • robust_list 用于实现 get_robust_list,set_robust_list 系统调用
  • rlimit_nofile 用于实现 rlimit 相关系统调用,但目前仅用于限制进程可打开的文件数
  • clear_child_tid 用于实现 fork,set_tid_address 系统调用

另外,我们在参考往届优秀作品实现时,有注意到有些队伍在 TCB 中保留了 user_stask 字段。该字段用于保存为线程分配用户栈时保留的 user_stack 虚拟地址。

BTD-OS 的 TCB 中并没有保存,原因是按照 man-page 关于 fork(clone) 系统调用的规定对于共享内存的子进程或子线程,创建传入 stask 参数,该参数规定了子进程/线程的用户栈的位置,故实际上不需要我们额外创建 user_stack。

对于非 forK(clone) 创建的进程(new/exec load_elf 方法中),BTD-OS 会分配并映射该进程的 user_stack。

// kernel/src/mm/memory_set.rs: fn load_elf
memory_set.user_stack_areas = VmArea::new(
    user_stack_bottom.into(),
    user_stack_top.into(),
    MapType::Framed,
    VmAreaType::UserStack,
    MapPermission::R | MapPermission::W | MapPermission::U,
    None,
    0,
);