# 基于内核栈切换的进程切换
## 实验内容
正如实验手册所写:
> 本次实践项目就是将 Linux 0.11 中采用的 TSS 切换部分去掉,取而代之的是基于堆栈的切换程序。具体的说,就是将 Linux 0.11 中的 switch_to 实现去掉,写成一段基于堆栈切换的代码。
> 本次实验内容如下:
- 编写汇编程序 `switch_to`
- 完成主体框架
- 在主体框架下依次完成 PCB 切换,内核栈切换,LDT 切换等
- 修改 `fork()`,由于是基于内核栈的切换,所以进程需要创建出能完成内核栈切换的样子
- 修改 PCB,即 `task_struct` 结构,增加相应的内容域,同时处理由于修改了 `task_struct` 所造成的影响
- 用修改后的 Linux 0.11 仍然可以启动,可以正常运行
- (选做)分析实验 3 的日志体会修改前后系统运行的差别
实验报告:
回答下面三个问题:
1. 针对下面的代码片段:
```c
movl tss,%ecx
addl $4096,%ebx
movl %ebx,ESP0(%ecx)
```
回答问题:(1) 为什么要加 4096;(2) 为什么没有设置 tss 中的 `ss0`
2. 针对代码片段:
```c
*(--krnstack) = ebp;
*(--krnstack) = ecx;
*(--krnstack) = ebx;
*(--krnstack) = 0;
```
回答问题:(1) 子进程第一次执行时,`eax=?` 为什么要等于这个数?哪里的工作让 `eax` 等于这样一个数?(2) 这段代码中的 `ebx` 和 `ecx` 来自哪里,是什么含义,为什么要通过这些代码将其写到子进程的内核栈中?(3) 这段代码中的 `ebp` 来自哪里,是什么含义,为什么要做这样的设置?可以不设置吗?为什么?
3. 为什么要在切换完 LDT 之后要重新设置 `fs=0x17`?而且为什么重设操作要出现在切换完 LDT 之后,出现在 LDT 之前又会怎么样?
## 实验过程
### 实验结果
修改为基于内核栈切换的进程切换后系统运行正常:
![](https://www.writebug.com/myres/static/uploads/2021/11/7/b647cde1084312b4968e72c8d539b5c9.writebug)
![](https://www.writebug.com/myres/static/uploads/2021/11/7/4633868436110bc13481d3c152ec4824.writebug)
### 实验分析
#### 1. 为什么要从基于 TSS 的任务切换改为基于内核栈切换的任务切换
Linux 0.11 利用 80x86 硬件提供的机制:通过执行 `ljmp next进程TSS描述符的选择符, (无用的)偏移地址` 指令来进行任务切换。这种切换机制的特点正如实验手册所说:
> 现在的 Linux 0.11 采用 TSS 和一条指令就能完成任务切换,虽然简单,但这条指令的执行时间却很长,在实现任务切换时需要 200 多个时钟周期(一个任务的时间片只有 15 个时钟周期)。而通过堆栈实现任务切换可能要更快,而且采用堆栈的切换还可以使用指令流水的并行优化技术,同时又使得 CPU 的设计变得简单。所以无论是 Linux 还是 Windows,进程/线程的切换都没有使用 Intel 提供的这种 TSS 切换手段,而都是通过堆栈实现的。
存在切换时间长,依赖 CPU 指令支持,单一指令切换无法使能指令流水的并行优化这些问题。而从课程的讲解我们知道:对于函数调用,依靠栈进行返回地址保存和弹栈返回操作;对于用户级线程,每个线程拥有一个线程控制块 TCB,TCB 关联着用户栈,TCB 切换引起用户栈跟着切换,实现从一个线程切换到另一个线程以及再次切换回这个被换出的线程;对于核心级线程,线程切换发生在内核,从用户态进入内核态首先要发生线程用户栈到内核栈的切换,线程的 TCB 关联着内核栈,TCB 切换引起内核栈切换,利用 `iret` 指令进行中断返回会引起内核栈中用户态参数的出栈,从而实现执行流程转移到新进程的用户态指令和用户栈。这些例子充分说明了栈在指令流程切换中的关键作用,再参照内核 `main` 函数完成初始化工作后,以模拟特权级发生变化的内核中断返回的方式手动切换到任务 0 执行,完全可以自己想出是可以利用内核栈切换的方式实现进程切换的。
#### 2. 是否还需要任务状态段 TSS
虽然不再使用 `ljmp TSS段选择符的选择子, (不使用的)段内偏移` 进行任务切换,但 Intel 的中断处理机制仍需要保持,因为 CPU 正是依靠这种机制才能在中断处理时找到内核栈,并将用户态下的 `SS:ESP, EFLAGS, CS:EIP` 这 5 个寄存器的值自动压入到内核栈中,这是沟通用户栈(用户态)和内核栈(内核态)的关键桥梁。具体处理过程参见 [3. 中断和异常的硬件处理](#3-%E4%B8%AD%E6%96%AD%E5%92%8C%E5%BC%82%E5%B8%B8%E7%9A%84%E7%A1%AC%E4%BB%B6%E5%A4%84%E7%90%86)。所以仍然需要有一个当前 TSS,这个 TSS 就是需要我们额外定义的全局变量 tss,即 0 号进程的 tss,所有进程都共用这个 tss,任务切换时不再发生变化。
#### 3. 中断和异常的硬件处理
在实验 2:系统调用的从 Linux 0.11 自带的库函数入手追寻系统调用的实现过程]介绍的中断或异常的处理过程我们已经知道,当特权级发生变化时,也就是说,当前特权级 CPL(存放在 CS 寄存器的低两位)不同于所选择的段描述符的 DPL(从 GDT 表中获取的段描述符中的描述符特权级 DPL),控制单元必须开始使用与新的特权级相关的栈,要发生栈的切换。通过执行以下步骤来做到这点:
1. 读 `TR` 寄存器,里面保存了 `TSS` 段的选择子,利用该选择子在 GDT 表中找到运行进程的 TSS 段的内存位置。
2. 在 `TSS` 段中找到新特权级相关的栈段和栈指针即 `ss0` 和 `esp0`,将它们装载到 `ss` 和 `esp` 寄存器。
3. 在新的栈中保存 `ss` 和 `esp` 以前的值,这些值定义了旧特权级相关的栈的逻辑地址。形象点说,就是在新栈和旧栈之间拉了一条线,形成了一套栈。
4. 如果故障已发生,用引起异常的指令地址装载 `cs` 和 `eip` 寄存器,从而使得这条指令能再次执行。
5. 在栈中保存 `eflags`,`cs`,`eip` 的内容。
6. 装载 `cs` 和 `eip` 寄存器,其值分别是 IDT 表中由中断号指定的门描述符的段选择符和偏移量字段。这些值给出了中断或异常处理程序的第一条指令的逻辑地址。
而在中断或异常处理完毕后,相应的处理程序必须产生一条 `iret` 指令,把控制权转交给被中断的进程,这将迫使控制单元:
1. 用保存在栈中的值装载 `cs`,`eip` 和 `eflags` 寄存器。如果一个硬件出错码曾被压入栈中,并且在 `eip` 内容的下面,那么,在执行 `iret` 指令前必须先弹出这个硬件出错码。
2. 检查处理程序的 CPL 是否等于 `cs` 中最低两位的值(这意味着被中断的进程与处理程序运行在同一特权级)。如果是,`iret` 终止执行;否则,转入下一步。
3. 从栈中装载 `ss` 和 `esp` 寄存器,因此,返回到与旧特权级相关的栈。
4. 检查 `ds`,`es`,`fs` 及 `gs` 段寄存器的内容,如果其中一个寄存器包含的选择符是一个段选择符,并且其 DPL 的值小于 `CPL`,那么,清相应的段寄存器。控制单元这么做是为了禁止用户态的程序(CPL=3)利用内核以前所用的段寄存器(DPL=0)。如果不清这些寄存器,怀有恶意的用户态程序就可能利用它们来访问内核地址空间。
#### 4. 进程切换的五段论
##### 4.1 利用中断进入内核引起用户栈到内核栈的切换
前面 [3. 中断和异常的硬件处理](#3-%E4%B8%AD%E6%96%AD%E5%92%8C%E5%BC%82%E5%B8%B8%E7%9A%84%E7%A1%AC%E4%BB%B6%E5%A4%84%E7%90%86)已经详细说明了:**利用中断进入内核时 CPU 通过 TR 寄存器找到 TSS 的内存位置,利用里面的 `ss0` 和 `esp0` 的值设置好内核栈(此时内核栈是空的,esp0 应该设置为内核栈的栈顶地址),将用户栈的 `ss` 和 `esp` 的值压入
没有合适的资源?快使用搜索试试~ 我知道了~
温馨提示
编写汇编程序 switch_to 完成主体框架 在主体框架下依次完成 PCB 切换,内核栈切换,LDT 切换等 修改 fork(),由于是基于内核栈的切换,所以进程需要创建出能完成内核栈切换的样子 修改 PCB,即 task_struct 结构,增加相应的内容域,同时处理由于修改了 task_struct 所造成的影响 用修改后的 Linux 0.11 仍然可以启动,可以正常运行 (选做)分析实验 3 的日志体会修改前后系统运行的差别
资源推荐
资源详情
资源评论
收起资源包目录
基于汇编语言实现内核栈切换的进程切换【100013093】 (134个子文件)
namei.c 16KB
sched.c 15KB
console.c 14KB
floppy.c 11KB
memory.c 11KB
exec.c 9KB
buffer.c 9KB
hd.c 8KB
tty_io.c 7KB
malloc.c 7KB
inode.c 7KB
super.c 5KB
main.c 5KB
fork.c 5KB
build.c 5KB
exit.c 5KB
testlab2.c 5KB
tty_ioctl.c 5KB
traps.c 5KB
vsprintf.c 5KB
open.c 4KB
process.c 4KB
bitmap.c 4KB
sys.c 4KB
ll_rw_blk.c 3KB
read_write.c 3KB
ramdisk.c 3KB
signal.c 3KB
pipe.c 2KB
char_dev.c 2KB
printk.c 2KB
file_dev.c 2KB
fcntl.c 1KB
mktime.c 1KB
block_dev.c 1KB
serial.c 1KB
who.c 1KB
ctype.c 1KB
who_cp.c 1KB
stat.c 1KB
truncate.c 1KB
math_emulate.c 1023B
ioctl.c 982B
panic.c 463B
iam.c 392B
open.c 389B
whoami.c 309B
wait.c 253B
_exit.c 198B
string.c 177B
execve.c 170B
write.c 160B
close.c 131B
setsid.c 128B
dup.c 127B
file_table.c 122B
errno.c 73B
sched.h.cur1 6KB
string.h 7KB
unistd.h 6KB
sched.h 6KB
a.out.h 6KB
fs.h 5KB
termios.h 5KB
blk.h 3KB
sys.h 3KB
fdreg.h 2KB
tty.h 2KB
hdreg.h 2KB
signal.h 2KB
system.h 2KB
fcntl.h 1KB
segment.h 1KB
stat.h 1KB
config.h 1KB
errno.h 1KB
ctype.h 1KB
kernel.h 865B
types.h 805B
stdarg.h 780B
time.h 734B
wait.h 560B
memory.h 492B
io.h 477B
const.h 321B
head.h 304B
stddef.h 286B
utsname.h 234B
utime.h 225B
mm.h 219B
times.h 200B
LICENSE 1KB
process.log 39KB
process_analyze.log 33KB
process.log 7KB
Makefile 5KB
Makefile 4KB
Makefile 3KB
Makefile 3KB
Makefile 2KB
共 134 条
- 1
- 2
资源评论
神仙别闹
- 粉丝: 3806
- 资源: 7471
下载权益
C知道特权
VIP文章
课程特权
开通VIP
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
最新资源
- 排球场地的排球识别 yolov7标记
- DOTA 中的 YOLOX 损失了 KLD (定向物体检测)(Rotated BBox)基于YOLOX的旋转目标检测.zip
- caffe-yolo-9000.zip
- Android 凭证交换和更新协议 - “你只需登录一次”.zip
- 2024 年 ICONIP 展会.zip
- 微信小程序毕业设计-基于SSM的电影交流小程序【代码+论文+PPT】.zip
- 微信小程序毕业设计-基于SSM的食堂线上预约点餐小程序【代码+论文+PPT】.zip
- 锐捷交换机的堆叠,一个大问题
- 微信小程序毕业设计-基于SSM的校园失物招领小程序【代码+论文+PPT】.zip
- MATLAB《结合萨克拉门托模型和遗传算法为乐安河流域建立一个水文过程预测模型》+项目源码+文档说明
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功