1.回顾
程序开始执行,PC置初值,CPU就不断地 取指执行
顺序执行,CPU如果遇到 耗时的指令,就在等待,没有充分利用,引出了 任务切换
1.1 栈实现跳转
1.2 一个栈 + Yield 造成的混乱
D执行完,回不到 204
1.3 两个栈 + 两个用户TCB
切换时,Yield找到 新的用户线程的TCB,找到新的用户栈,切换
1.4 内核栈切换
用户栈 关联着 内核栈,切换时,从用户栈 到 内核栈,到TCB,切换 内核栈,再实现 切换 用户栈
2.内核栈切换的实现:屏幕交替打印 A 和 B
2.1 AB.c
main()
{
//打印 A
if(!fork())
{
while(1)
printf("A");
}
// 打印 B
if(!fork())
{
while(1)
printf("B");
}
wait();
}
2.2 计算机执行过程
fork:
mov __NR_fork, %eax
int 0x80
set_system_gate(0x80, &system_call)
system_call:
sys_fork
100: mov %eax, res
cmpl res, 0
jne 208
cmpl $0, state(current)
jne reschedule
iret
sys_fork:
pushl ...
call copy_process
ret
200: printf("A")
jmp 200
208: ...
304: wait()
fork中有int 0x80中断,通过中断进入内核,形成了 对应的用户栈 和 内核栈,执行 system_call
system_call 先调用 sys_fork,sys_fork 调用 copy_process
copy_process 会在内核中 做一个新的PCB,做出新的 内核栈,eip 对应的代码就是 打印A
fork出新的进程,cmpl res, 0,res == 0 执行 200:打印A,res !=0,执行208
执行完 copy_process,父进程就要返回了,cmpl $0, state(current),执行 reschedule,屏幕打印A
创建完了 打印A 的进程,没有阻塞,还有时间片,就可以创建 下一个 子进程了
创建 打印B的进程 与 打印A的进程类似,只是 eip 对应的是 打印B的指令
现在有2个子进程都在 就绪队列中
创建了 2个 子进程,main 执行 wait,开始等待,就是将自己的state 设为 阻塞态,调用 schedule, switch_to 切换到下一个进程,switch_to通过 TSS 完成栈的切换,当前线程的 TSS = CPU的 TSS,CPU的 TSS = 下一个线程的
main(){
...
wait();// 又是 mov __NR_wait int 0x80
system_call:
call sys_waitpid
sys_waitpid() // exit.c中
current->state=TASK_INTERRUPTIBLE;
schedule();
schedule(){
if((*p)->state == TASK_RUNNING && (*p)->counter > c)
c = (*p)->counter, next = i;
...
switch_to(next);
}
A执行打印A,B执行打印B,怎么能交替执行呢?
2.3 时钟中断实现交替打印
需要在内核中有一个调度点,比如通过 时钟中断
// shced.c
void shed_init(void)
{
set_intr_gate(0x20, &timer_interrupt);
}
void _timer_interrupt:
...
call do_timer
void do_timer(...)
{
if((--current->counter > 0))
return;
current-> counter = 0;
schedule();
}
时钟中断,每到中断,执行 do_timer,counter–,当counter == 0,调 schedule,调度一次
main()
{
mov __NR_fork, %eax
int 0x80
100 : mov %eax, res
cmpl res, 0
jne 208
200: printf("A")
jmp 200
208:... 300 :printf("B")
jmp 300
308: wait()
}
设 counter = 0,时间片用完了,A不执行,调用schedule 进程 switch_to 切换到B
B.eip = 300, A.eip = 200
B切换到A,还需要中断