1 Star 0 Fork 0

WHC/xv6-course

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
33-context-switch.org 7.54 KB
一键复制 编辑 原始数据 按行查看 历史
殊蕤 提交于 2023-11-16 09:07 . polish

swtch上下文切换及内核态执行

上个视频

对于多任务 OS 来说,TSS segment 是必不可少的,系统至少需要一个 TSS segment,但是 现在的 OS 系统不使用 TSS 机制来进行任务的切换。

情景提示:

  1. TSS 存在的唯一理由是:需要提供 0 ~ 2 权限级别的 stack pointer,当发生 stack 切换时,必须使用 TSS 提供的相应的 stack pointer
  2. 若提供空的 TSS segment,或者可以考虑以直接传递 stack pointer 的方式实现 stack 切换,即便是这样设计 processor 要读取 TSS segment 这一工作是必不可少的

swtch 切换进程上下文

切换 kernel 上下文到当前进程

void swtch(struct context** old, struct context* new);
swtch(&(c->scheduler), p->context);
  1. 传值 old, new
  2. 保存旧的寄存器 struct context
  3. 上下文切换
  4. 恢复新的寄存器

../../study/os/xv6-public/swtch.S

swtch(&(c->scheduler), p->context);
(gdb) p/x &(c->scheduler)
$1 = 0x801117a4
(gdb) p/x  p->context
$2 = 0x8dffff9c
(gdb) p/x $eip
$3 = 0x80103c92
(gdb) info reg eax
eax            0x801117a4          -2146363484
(gdb) info reg edx
edx            0x8dffff9c          -1912602724
(gdb)

1. Initial
  eax = 0x801117a4 old &(c->scheduler)
  edx = 0x8dffff9c new p->context

  | ???             |
  | ???             |
  | eip(0x80103c97) | <- esp
  |                 |

2. Save
  | ???             |
  | ???             |
  | eip(0x80103c97) |
  | ebp             |
  | ebx             |
  | esi             |
  | edi             | <- esp (0x8011544c)

3.1 Switch stack: set eax
  | ???             |
  | ???             |
  | eip(0x80103c97) |
  | ebp             |
  | ebx             |
  | esi             |
  | edi             | <- esp (0x8011544c)

  # *(c->scheduler) = 0x8011544c

3.2 Switch stack: set esp <= edx
  | ??????????????? |
  | ??????????????? |
  | ??????????????? |
  | ??????????????? |
  | ??????????????? |
  | ??????????????? |
  | ??????????????? | <- esp (0x8dffff9c)

  # *(c->scheduler) = 0x8011544c

3.3 Switch stack: 脑补 p->context
  | ??????????????? |
  | ??????????????? |
  | eip             |
  | ebp             |
  | ebx             |
  | esi             |
  | edi             | <- esp (0x8dffff9c)

  # *(c->scheduler) = 0x8011544c

4. Pop
  | ??????????????? |
  | ??????????????? |
  | eip (0x80103880)| <- esp (0x8dffffac)
  | ebp             |
  | ebx             |
  | esi             |
  | edi             |

  # *(c->scheduler) = 0x8011544c
  # 0x80103880 <forkret>

5. ret
  => forkret

forkret 初始化工作

调用 swtch 前的堆栈

(gdb) b swtch
Breakpoint 1 at 0x8010487b: file swtch.S, line 11.
(gdb) c
Continuing.
The target architecture is set to "i386".
=> 0x8010487b <swtch>:  mov    0x4(%esp),%eax

Thread 1 hit Breakpoint 1, swtch () at swtch.S:11
11        movl 4(%esp), %eax
(gdb) bt
#0  swtch () at swtch.S:11
#1  0x80103c97 in scheduler () at proc.c:343
#2  0x8010303f in mpmain () at main.c:57
#3  0x8010318c in main () at main.c:37
(gdb) si
=> 0x8010487f <swtch+4>:        mov    0x8(%esp),%edx
12        movl 8(%esp), %edx
(gdb)
  • swtch.S 返回 ret 时的,跳转到 forkret
  • allocproc 时设置的返回值 $eip
(gdb) si
=> 0x80103880 <forkret>:        push   %ebp
forkret () at proc.c:398
398       release(&ptable.lock);
(gdb) bt
#0  forkret () at proc.c:398
(gdb)

trapret 陷阱返回

trapret 时 forkret 返回的 pc, 在 trapret 中调用 iret 将控制权交给内核

(gdb) b forkret
Breakpoint 1 at 0x80103880: forkret. (2 locations)
(gdb) c
Continuing.
The target architecture is set to "i386".
=> 0x80103880 <forkret>:        push   %ebp

Thread 1 hit Breakpoint 1, forkret () at proc.c:398
398       release(&ptable.lock);
(gdb) n
=> 0x80103890 <forkret+16>:     mov    0x8010a000,%eax
400       if (first) {
(gdb) si
=> 0x80103895 <forkret+21>:     add    $0x10,%esp
0x80103895      400       if (first) {
(gdb)
=> 0x80103898 <forkret+24>:     test   %eax,%eax
0x80103898      400       if (first) {
(gdb)
=> 0x8010389a <forkret+26>:     jne    0x801038a0 <forkret+32>
0x8010389a      400       if (first) {
(gdb)
=> 0x801038a0 <forkret+32>:     movl   $0x0,0x8010a000

Thread 1 hit Breakpoint 1, forkret () at proc.c:404
404         first = 0;
(gdb)
=> 0x801038aa <forkret+42>:     sub    $0xc,%esp
405         iinit(ROOTDEV);
(gdb) n
=> 0x801038b4 <forkret+52>:     movl   $0x1,(%esp)
406         initlog(ROOTDEV);
(gdb) n
=> 0x80105852 <trapret>:        popa
trapret () at trapasm.S:26
26        popal
(gdb) bt
#0  trapret () at trapasm.S:26
(gdb)

用于唤醒是通过中断,所以返回 trap 使得最终控制器交给操作系统

iret
;; 依次取出
pop eip
pop cs
popf
pop esp
pop ss
(gdb) x/4x $esp
0x8dffffec:     0x00000000      0x0000001b      0x00000200      0x00001000
(gdb) set print pretty
(gdb) p/x *(struct trapframe *)0x8dffffb4
$4 = {
  edi = 0x0,
  esi = 0x0,
  ebp = 0x0,
  oesp = 0x0,
  ebx = 0x0,
  edx = 0x0,
  ecx = 0x0,
  eax = 0x0,
  gs = 0x0,
  padding1 = 0x0,
  fs = 0x0,
  padding2 = 0x0,
  es = 0x23,
  padding3 = 0x0,
  ds = 0x23,
  padding4 = 0x0,
  trapno = 0x0,
  err = 0x0,
  eip = 0x0,
  cs = 0x1b,
  padding5 = 0x0,
  eflags = 0x200,
  esp = 0x1000,
  ss = 0x23,
  padding6 = 0x0
}
(gdb)

initcode 执行地址空间观察

虚拟地址数据

(gdb) xv-v2p 0
Prepare: VA=0x00000000 PDX=0x00000000 PTX=0x00000000 OFFSET=0x00000000
Stage 1: CR3=0x0dffe000 PDX=0x00000000 ADDR1=0x0dffe000 PDE=0x0dfbc027
Stage 2: PPN=0x0dfbc000 PTX=0x00000000 ADDR2=0x0dfbc000 PTE=0x0dfbd067
Final  : PPN2=0x0dfbd000 OFFSET=0x00000000 PA=0x0dfbd000
Summary: VA=0x00000000 -> PA=0x0dfbd000
(gdb) x/16h 0
0x0:    0x2468  0x0000  0x6800  0x001c  0x0000  0x006a  0x07b8  0x0000
0x10:   0xcd00  0xb840  0x0002  0x0000  0x40cd  0xf7eb  0x692f  0x696e
(gdb)

物理地址数据

(qemu) info tlb
0000000000000000: 000000000dfbd000 -------UW
0000000080000000: 0000000000000000 --------W
0000000080001000: 0000000000001000 --------W
0000000080002000: 0000000000002000 --------W
...
(qemu) info mem
0000000000000000-0000000000001000 0000000000001000 urw
0000000080000000-0000000080100000 0000000000100000 -rw
0000000080100000-0000000080108000 0000000000008000 -r-
0000000080108000-000000008e000000 000000000def8000 -rw
00000000fe000000-0000000100000000 0000000002000000 -rw
(qemu) xp/16h 0x0dfbd000
000000000dfbd000: 0x2468 0x0000 0x6800 0x001c 0x0000 0x006a 0x07b8 0x0000
000000000dfbd010: 0xcd00 0xb840 0x0002 0x0000 0x40cd 0xf7eb 0x692f 0x696e
(qemu)

磁盘镜像数据

xv6-public $ hexdump -n 32 initcode
0000000 2468 0000 6800 001c 0000 006a 07b8 0000
0000010 cd00 b840 0002 0000 40cd f7eb 692f 696e
0000020
xv6-public $

总结

  1. swtch => p->context
  2. ret => forkret
    • allocproc
  3. forkret => trapret
  4. trapret iret => eip(0x0)
    • user space
    • 开始执行 initcode.S
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
C
1
https://gitee.com/whc_softHardware/xv6-course.git
git@gitee.com:whc_softHardware/xv6-course.git
whc_softHardware
xv6-course
xv6-course
master

搜索帮助

D67c1975 1850385 1daf7b77 1850385