目录
【前言】
在李治军老师的《操作系统》课程的实验 5(基于内核栈切换的进程切换)中,需要完成 switch_to 函数的汇编代码编写。代码很容易获取,网上的资源非常多,但是拿到了看不懂……
所以本文章将会对 switch_to 的代码进行逐条分析,希望能帮助理解基于内核栈的进程切换的整体流程。
switch_to() 完整汇编代码:
.align 2
switch_to:
// 因为该汇编函数要在c语言中调用,所以要先在汇编中处理栈帧
pushl %ebp
movl %esp,%ebp
pushl %ecx
pushl %ebx
pushl %eax
// 将ebp+8指向的数据(目标进程的PCB)传递给ebx,然后进行判断:
// 如果目标进程的pcb <<等于>> 当前进程的pcb => 不需要进行切换,直接退出函数调用
// 如果目标进程的pcb <<不等于>> 当前进程的pcb => 需要进行切换,直接跳到下面去执行
movl 8(%ebp),%ebx
cmpl %ebx,current
je 1f
/** 执行到此处,就要进行真正的基于堆栈的进程切换了 **/
// 切换PCB
movl %ebx,%eax
xchgl %eax,current
// 重写TSS中内核栈的指针
movl tss,%ecx
addl $4096,%ebx
movl %ebx,ESP0(%ecx)
// 切换内核栈
movl %esp,KERNEL_STACK(%eax)
movl 8(%ebp),%ebx
movl KERNEL_STACK(%ebx),%esp
// 切换LDT
movl 12(%ebp),%ecx
lldt %cx
// 切换 LDT 之后
movl $0x17,%ecx
mov %cx,%fs
// 这一段先不用管
cmpl %eax,last_task_used_math
jne 1f
clts
// 现在进入新进程的内核栈工作了,所以接下来做的四次弹栈以及ret处理使用的都是新进程内核栈中的东西
1: popl %eax
popl %ebx
popl %ecx
popl %ebp
ret
一、栈帧的处理
1. 什么是栈帧
大多数 CPU 上的程序实现都是通过使用栈来支持函数调用操作。栈被用来传递函数参数、存储返回地址、临时保存寄存器原有值以备恢复以及用来存储局部数据。单个函数调用操作所使用的栈部分被称为栈帧结构。
一个函数栈帧结构的两端由两个指针来指定:① ebp:用作帧指针(指向栈帧底部);② esp:用作栈指针(指向栈帧顶部)。在函数执行过程中,肯定会有数据的入栈和出栈,而栈指针 esp 就会随之变化(esp 始终指向栈顶)。因此,函数中对大部分数据的访问都是基于帧指针 ebp 进行的。
>> 强烈推荐先看看这篇文章再继续:栈帧_yxysdcl的博客-优快云博客
2. 为什么要处理栈帧
现在我们知道,每个函数的每次调用,都有它自己独立的一个栈帧,这个栈帧中保存着该函数所需要的各种信息。寄存器 ebp 指向当前栈帧的底部,寄存器 esp 指向当前栈帧的顶部。
当调用一个函数时,就意味着要创建一个属于这个函数自己的栈帧。而进入该函数后这个栈帧就变成了当前栈帧,也就是说退出上一个函数的栈帧进入新的栈帧。所以要让 ebp 重新指向当前栈帧的底部,让 esp 重新指向当前栈帧的顶部,同时还要保存好上一个函数的栈帧底部和栈帧顶部。
3. 执行 switch_to 前的内核栈
现在我们应该清楚,在执行上面的 switch_to 汇编代码前,当前进程内核栈的情况应该如下图所示(我们以在 schedule 中调用 switch_to 为例)。此时还没有进入 switch_to 函数,所以 ebp 和 esp 应该分别指向 schedule 函数的栈帧底部和栈帧顶部(栈帧底部具体在哪儿就不用管了)。
schedule() 调用 switch_to() 的时候会依次将 switch_to 的参数(从右至左):_LDT(next) 和 pnext,以及 switch_to 的返回地址:{ 依次入栈。

4. 栈帧处理代码分析
接下来我们逐条分析 switch_to 中栈帧处理的部分。
pushl %ebp
>> 将 ebp 入栈 <<
这条指令的目的就是保存 schedule() 的栈帧底部。因为现在进入了 switch_to 函数,也就是说要切换到 switch_to 的栈帧了,所以必须把上面函数的栈帧底部和栈帧顶部保存起来,这样 switch_to 结束返回时才能成功返回到之前调用函数的位置。ps:这里不用另外保存栈顶,因为上一个栈帧的顶部就是下一个的栈帧的底部(两栈帧相邻)。
所以 ebp 入栈后内核栈变为:(这里 switch_to 的栈帧底部和顶部是一样的)

movl %esp,%ebp
>> 将 esp 中内容传递给 ebp <<
原来 ebp 指向 sched

本文详细分析了基于内核栈切换的进程切换过程,从栈帧处理、PCB比较、PCB切换、TSS内核栈指针重写、内核栈切换、LDT切换等方面展开,阐述了操作系统如何在内核层面实现进程间的切换。通过对`switch_to`函数的汇编代码逐行解读,揭示了进程上下文切换的底层原理。
最低0.47元/天 解锁文章
3173






