操作系统
第八章
第八章的主题是线程、同步与死锁检测。到 ch7 为止,系统已经有进程、地址空间、文件系统、管道、重定向和信号等能力;ch8 进一步解决“同一个进程内部如何拥有多条并发执行流,以及这些执行流如何安全共享资源”的问题。
ch8 的核心模型变化可以概括为:线程是调度单位,进程是资源容器。Process 保存 pid、address_space、fd_table、signal、semaphore_list、mutex_list、condvar_list 等共享资源;Thread 保存 tid 和 ForeignContext,负责记录当前执行到哪里。
同一进程内的线程共享地址空间和文件描述符表,因此可以直接访问同一份堆数据、全局变量和 fd;但每个线程必须拥有独立用户栈,因为栈保存函数调用现场、局部变量、返回地址和临时寄存器。如果多个线程共用一个栈,就会互相覆盖调用链。
thread_create(entry, arg) 的流程是:找到当前进程,在当前进程地址空间里分配新用户栈,构造用户态上下文,设置入口 PC=entry、sp=新栈顶、a0=arg,用同一个 satp 创建 Thread,并把它加入 PThreadManager。新线程没有新建进程,也没有复制地址空间。
多线程共享内存会带来竞态条件。比如两个线程同时执行 counter += 1,可能都读到旧值再写回相同的新值,导致计数丢失。因此 ch8 引入 mutex、semaphore、condvar 三类同步原语:mutex 保护临界区,semaphore 管理计数型资源,condvar 用于等待某个条件成立。
mutex_lock、semaphore_down、condvar_wait 在资源不可用时会返回 -1,主调度循环据此 make_current_blocked,把当前线程从 ready queue 中移除。mutex_unlock、semaphore_up、condvar_signal 释放资源或发送信号时,可能返回一个等待线程 tid,内核再 re_enque(tid) 把它放回就绪队列。
条件变量必须和 mutex 一起理解。典型语义是:线程持有 mutex,发现条件不满足,于是 condvar_wait 释放 mutex 并阻塞;其他线程修改条件后 condvar_signal 唤醒等待者;等待线程醒来后重新竞争 mutex 并继续检查条件。关键点是等待时释放锁,唤醒后不能跳过条件检查。
ch8 exercise 要求实现 enable_deadlock_detect,syscall ID 为 469。开启后,mutex_lock 和 semaphore_down 在真正阻塞前要判断这次申请是否会导致死锁风险;如果会导致不安全状态,就拒绝申请并返回 -0xDEAD,避免线程永远互相等待。
死锁检测采用类似银行家算法的安全性检查:Available 表示剩余资源,Allocation 表示每个线程已持有资源,Need 表示每个线程还需要多少资源。检测时用 Work 模拟可用资源,不断寻找 Need <= Work 的线程并假设它完成释放资源;如果最终所有线程都能 Finish,系统安全,否则存在死锁风险。
ch8_usertest 会依次运行 threads、threads_arg、mpsc_sem、sync_sem、race_adder_mutex_blocking、phil_din_mutex、test_condvar、pipetest、ch8_deadlock_mutex1、ch8_deadlock_sem1、ch8_deadlock_sem2 等测试,覆盖线程创建、参数传递、同步、条件变量、管道回归和死锁检测。
一句话总结 ch8:它把原来“进程既是资源容器又是执行单元”的模型拆成 Process + Thread,并用同步原语、阻塞唤醒和死锁检测把多线程共享资源这件事变得可控。
GitHub 仓库:flyyansii/Tg-rCore-Tutorial-2026S
