进程与线程:04 内核线程
内核级线程概述
上一讲我们学习了用户级线程,了解了其切换和创建方式。用户级线程切换核心在于从一个栈变为两个栈,每个线程有自己的栈和线程控制块(tcb),切换时先切换tcb再切换栈,创建时将切换的pc指针放入栈中并创建tcb。
本讲聚焦内核级线程。进程切换本质上是切换内核级线程,因为进程在内核中运行,且进程分配资源、访问内存等操作需在内核态完成,用户态无法进行。
核心级线程的优点
从多核角度来看,多核处理器中每个CPU有自己的缓存和内存管理单元(mmu),而多核共享同一个mmu。一套映射表对应多个执行序列符合线程特征,只有在内核中才能将线程分配到具体的物理设备(如CPU核)上,实现多核并行。用户级线程操作系统无法感知,不能分配硬件资源,多进程切换mmu开销大,无法充分发挥多核价值,所以核心级线程有其优势。
核心级线程与用户级线程的区别
用户级线程使用两个栈,而核心级线程使用两套栈,即每个线程要有自己的用户栈和内核栈。因为核心级线程需进入内核态,在用户态执行时用用户栈,进入内核态时需内核栈用于函数调用等操作。
内核栈相关原理
进入内核的唯一方式是中断,如int指令、鼠标键盘硬件中断、时钟中断等。中断发生时,硬件自动启用内核栈,并将用户态执行时的栈段寄存器ss、栈指针sp,以及代码段寄存器cs、指令指针ip等压入内核栈,通过指针将用户栈和内核栈关联起来。中断返回(iret)时,弹出寄存器内容,回到用户栈和用户态继续执行。
核心级线程切换示例
以函数调用为例,在用户栈执行时调用read库函数,会展开成int指令。执行int指令时,硬件做好相关操作,将控制权转移到内核栈。
在内核中执行启动磁盘读操作可能导致阻塞,进而引发调度。调度时找到下一个线程的tcb,通过“switch to”操作切换内核栈指针,完成内核栈切换。切换后执行iret指令,回到用户态继续执行线程的用户态代码。
切换五段论
- 中断:中断是进入内核的开端,只有通过中断才能为切换做好准备,拉好相关链路。
- 中断处理:在中断处理过程中,可能因阻塞等情况引发调度,如时钟中断可能引出切换,调度时需找到下一个要执行的tcb。
- 根据tcb切换内核栈:找到tcb后,通过“switch to”操作完成内核栈的切换。
- 内核栈切换完成:完成内核栈切换后,为中断返回做准备。
- 中断返回:执行iret指令,完成用户栈和用户态执行地址的切换,回到用户态继续执行。
线程创建相关
从ThreadCreate开始创建线程,调用get_free_page 函数。该函数负责申请内存空间创建tcb,以及创建内核栈和用户栈并关联栈和tcb等操作。同时,将执行地址等信息放入tcp中,设置线程状态为可运行(TASK_RUNNING),为线程执行做准备。
总结
本讲详细讲解了内核级线程,包括其与用户级线程的区别、切换原理及创建方式等。内核级线程切换涉及中断、tcb切换、内核栈切换和用户栈切换等多个环节,通过五段论进行梳理。创建内核级线程需申请内存、初始化栈和tcb等。理解这些内容对掌握操作系统中线程相关机制至关重要,后续课程将进一步剖析Linux 0.11中关于进程(与内核级线程密切相关)的代码。 同时对比了用户级线程和内核线程在利用多核性质、并发度、代价、内核改动、灵活性等方面的差异,强调两者在操作系统中都有价值。学完本部分内容,应能在不支持线程的操作系统上尝试实现支持用户级线程或核心级线程的代码,而这也是相关实验的目标,如在Linux 0.11上实现内核级线程,虽然难度较大,但对真正学会操作系统意义重大。