1. 当执行完 system_interrupt
函数,执行 153
行 iret
时,记录栈的变化情况。
system_interrupt
程序内容如下:
iret
用于在处理器状态转移期间从中断或异常处理程序返回到被中断的程序,还原被中断程序的执行环境,包括寄存器、堆栈以及特权级别的状态。
iret
指令执行以下操作:
-
从堆栈中弹出
EIP
寄存器的值,以恢复中断或异常处理程序返回到的下一条指令的地址。 -
从堆栈中弹出
CS
寄存器的值,以恢复中断或异常处理程序返回到的代码段。 -
从堆栈中弹出标志寄存器
EFLAGS
的值,以恢复标志寄存器的状态。 -
如果在中断或异常处理程序执行期间切换了堆栈,
iret
会从堆栈中弹出新的ESP
寄存器的值,以恢复原始堆栈。 -
根据从堆栈中弹出的
CS
寄存器的值,恢复到适当的特权级别。这允许在不同特权级别(如内核态和用户态)之间切换。
在0x17c处设置断点,然后运行程序
1)执行 153
行 iret
前
CPU 正在执行的下一条指令的内存地址EIP:0x17c(即将执行iret)
代码段寄存器CS:0x8(当前执行的代码段以及执行代码的特权级别为 0 内核态)
栈顶指针ESP:0xe4e
此时stack中的值从栈顶开始是:
0x10eb(即将弹出的EIP
寄存器的值)
0x000f (即将弹出的CS
寄存器的值)
0x0246(即将弹出的EFLAGS
寄存器的值)
0x0bd8(即将弹出的ESP
寄存器的值)
2)执行 153
行 iret
后
CPU 正在执行的下一条指令的内存地址EIP:0x10eb
代码段寄存器CS:0xf(当前执行的代码段以及执行代码的特权级别为 3 用户态)
栈顶指针ESP:0xbd8
2. 当进入和退出 system_interrupt
时,都发生了模式切换,请总结模式切换时,特权级是如何改变的?栈切换吗?如何进行切换的?
1)特权级
在进入和退出 system_interrupt
时发生了特权级的模式切换。
-
进入
system_interrupt
:-
用户态(特权级 3)切换到内核态(特权级 0),中断服务在内核态中执行。
-
处理器将特权级从 3 切换到 0,CS的值由
0xf
变为0x8
。
-
-
退出
system_interrupt
:-
内核态(特权级 0)切换回用户态(特权级 3)。
-
iret
执行之后,处理器将特权级从 0 切换到 3,CS的值由0x8
变为0xf
。
-
2)栈
在进入和退出 system_interrupt
时同时会发生栈的切换。
-
进入
system_interrupt
:-
发生堆栈切换,以便在内核态中使用内核堆栈,来保护用户堆栈的完整性。
-
-
退出
system_interrupt
:-
发生堆栈切换,以返回到原特权级的堆栈,并继续用户态的执行。
-
3)如何切换
这种特权级的切换和堆栈切换是操作系统内核和处理器硬件协同工作的结果。
-
保存当前上下文:在模式切换之前,当前执行的任务的上下文以及当前特权级需要被保存压入栈中
-
选择新特权级别:根据要切换到的特权级别,操作系统选择新的代码段描述符和堆栈描述符,切换堆栈和特权级,并将它们加载到相应的寄存器中。
-
在进入内核态时,通常会使用内核堆栈,以避免破坏用户堆栈。
-
在退出内核态时,特权级和堆栈状态会恢复,以确保程序的正常执行。
-
3. 当时钟中断发生,进入到 timer_interrupt
程序,请详细记录从任务 0
切换到任务 1
的过程。
timer_interrupt
的程序内容如下:
task0
:打印 A
task1
:打印B
在0x12c处设置断点,然后运行程序
-
如下图所示,可以发现程序执行
task0
打印了好几个A
,但是还没有打印过B
,即还未执行过task1
-
单步运行到CS:EIP 0x08:0x0149处准备执行指令
jmpf 0x30:0
,即远跳转到0x30:0,将一个 TSS 选择子(0x30)装入 CS,实际上就是为了将任务切换到这个 TSS。 -
0x30恰好是task1的TSS。
-
跳转之前输入
info tss
,记录任务切换前task0
的TSS
-
单步执行后,寄存器状态如下图CS:EIP 0xf:0x10f4,现在已经切换到任务1,由于任务1是第一次执行,直接跳转到了用户程序task1的入口处
-
输入
info tss
,查看此时的TSS,发现TSS变化为task1
的TSS,并且与寄存器中的值相同,这说明了任务切换时会根据对应任务的 TSS的各个字段修改寄存器。
4. 又过了 10ms
,从任务1切换回到任务 0
,整个流程是怎样的? TSS
是如何变化的?各个寄存器的值是如何变化的?
在0x15c处设置断点,然后运行程序
-
如下图所示,程序打印了
B
,说明程序执行了task1
-
此时的TSS如下图所示,与寄存器内容相同,说明已经将
task1
的上下文保存在task1
的TSS中
-
然后执行
jmpf 0x20:0
,将一个 TSS 选择子(0x20)装入 CS,即task0
的TSS,由于第一次任务切换时将寄存器现场保存到了task0
TSS里,因此将TSS切换回来后,CS:EIP 会指向第一次任务切换的下一条地址,也就是0x8:0x150。
-
输入info tss
5. 请详细总结任务切换的过程。
-
时钟中断触发:任务切换通常是由系统中的时钟中断
timer_interrupt
触发的。时钟中断以固定的时间间隔(每10毫秒)发生一次,它是多任务处理的触发点。 -
保存当前任务上下文:当时钟中断触发时,操作系统会执行时钟中断处理程序。在处理程序执行之前,当前正在执行的
taskA
的上下文会被保存。EAX、ECX、EDX、EFLAGS、ESP、CS、EIP等寄存器的状态会保存在taskA
的 TSS 中。 -
选择下一个任务:在时钟中断处理程序中,操作系统会选择下一个要执行的
taskB
,任务B的上下文信息存储在taskB
的TSS中。 -
加载下一个任务上下文:时钟中断处理程序通过
taskB
的 TSS 将taskB
的上下文信息(寄存器的值)加载到处理器中,处理器现在准备执行taskB
。 -
切换堆栈:如果
taskA
和taskB
使用不同的内核堆栈,堆栈指针寄存器ESP将从taskA
的内核堆栈切换到taskB
的内核堆栈,以确保taskB
可以正常执行内核代码。 -
特权级别切换:如果
taskA
和taskB
属于不同的特权级别,时钟中断处理程序会执行特权级别切换操作。 -
taskB 开始执行:
taskB
的上下文准备就绪并加载到处理器中,taskB
开始执行。 -
时钟中断返回:时钟中断处理程序执行完毕后,处理器返回到
taskB
的执行点,taskB
继续执行。
在这个过程中,TSS寄存器中的值将发生变化,以反映新任务的上下文。旧任务的上下文信息已经被保存,以便在未来的任务切换中恢复。其他寄存器的值也会根据任务上下文的不同而变化,以确保任务切换的正确执行。这个过程允许多个任务在同一个系统中轮流执行,实现多任务处理。