操作系统在进行线程切换时,需要执行以下关键步骤:
1. 触发上下文切换的事件
- 中断:如时钟中断(时间片耗尽)、I/O完成中断等。
- 系统调用:线程主动请求阻塞(如等待I/O)。
- 资源竞争:线程因锁或信号量进入阻塞状态。
2. 保存当前线程的上下文
- 寄存器状态:保存程序计数器(PC)、堆栈指针(SP)、通用寄存器、状态寄存器(如EFLAGS)等到当前线程的线程控制块(TCB)。
- 扩展状态:保存浮点寄存器、向量寄存器(如SSE/AVX)等(若系统支持)。
- 内核栈指针:记录当前线程的内核栈位置,用于后续恢复。
3. 更新当前线程状态
- 将当前线程从运行状态改为就绪状态(时间片耗尽)或阻塞状态(等待资源)。
- 将其移入对应的队列(如就绪队列、阻塞队列)。
4. 调度新线程
- 调用调度器:根据调度算法(如轮转法、优先级调度)选择下一个运行的线程。
- 更新状态:将新线程标记为运行状态,并从队列中移出。
5. 恢复新线程的上下文
- 加载寄存器:从新线程的TCB中恢复PC、SP、通用寄存器等。
- 切换内核栈:将内核栈指针指向新线程的内核栈。
6. 更新系统状态
- 当前线程指针:更新内核数据结构(如
current_task
)指向新线程。 - 内存管理:若切换跨进程(如不同进程的线程),需切换地址空间(如更新页表基址寄存器)。
7. 返回用户模式
- 恢复执行:通过
iret
类指令返回到用户模式,从新线程的PC处继续执行。
8. 其他潜在操作
- 刷新TLB:仅跨进程切换时需要(同一进程内线程切换通常无需此操作)。
- 定时器重置:设置下一次时钟中断以管理时间片。
- 处理延迟状态:如延迟加载浮点寄存器(某些系统优化)。
关键区别:线程切换 vs 进程切换
- 地址空间:线程切换(同一进程内)无需切换地址空间;进程切换需切换页表。
- 资源开销:线程切换更快,因共享内存、文件等资源。
示例流程(x86架构)
1. 中断触发 → CPU跳转到内核中断处理程序。
2. 保存用户态寄存器到当前线程的内核栈。
3. 保存剩余寄存器到TCB,更新线程状态。
4. 调度器选择新线程,加载其TCB中的上下文。
5. 切换内核栈至新线程的内核栈。
6. 恢复新线程的寄存器,更新CR3(仅跨进程时)。
7. iret指令返回用户态,新线程开始执行。
总结
线程切换的核心是保存与恢复执行上下文,依赖TCB管理线程状态,通过调度器决策切换目标,并结合硬件机制高效完成。这一过程需兼顾性能与正确性,确保多任务并发执行的透明性。