《操作系统》by李治军 | 实验5.pre - switch_to 汇编代码详解

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

目录

【前言】

一、栈帧的处理

1. 什么是栈帧

2. 为什么要处理栈帧

3. 执行 switch_to 前的内核栈

4. 栈帧处理代码分析

二、PCB 的比较

1. 根据 PCB 判断进程切换与否

2. PCB 比较代码分析

三、PCB 的切换

1. 什么是 PCB 的切换

2. PCB 切换代码分析

四、TSS 内核栈指针的重写

1. 为什么要重写 TSS 中的内核栈

2. 内核栈重写代码分析

五、内核栈的切换

1. 如何完成内核栈切换

2. 内核栈切换代码分析

六、LDT 的切换

1. LDT 切换代码分析

七、用户栈的切换

1. switch_to 退出代码分析


【前言】

       在李治军老师的《操作系统》课程的实验 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

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值