实验过程
make
粘贴代码
修改代码
make通过
运行
代码分析
- 启动进程0的关键代码:
asm volatile(
"movl %1,%%esp
" /* set task[pid].thread.sp to rsp */
"pushl %1
" /* push rbp */
"pushl %0
" /* push task[pid].thread.ip */
"ret
" /* pop task[pid].thread.ip to rip */
:
: "c" (task[pid].thread.ip),"d" (task[pid].thread.sp)
);
由于这里是单线程的进程所以在调度的时候,实际是对单个线程环境维护(线程是调度的基本单位),也就是一个栈空间的sp与一个代码段的ip,在启动的时候该线程的栈空间是空的,所以对应之前的栈顶就是对应的栈底,并且栈顶等于栈底,而要想让其运行则需要将ip寄存器修改为对应线程的thread.ip,这里使用的是ret的方式,先将ip入栈之后ret设置。
- 进程调度的关键代码
asm volatile(
"pushl %%ebp
" /* save rbp of prev */
"movl %%esp,%0
" /* save rsp of prev */
"movl %2,%%esp
" /* restore rsp of next */
"movl $1f,%1
" /* save rip of prev */
"pushl %3
"
"ret
" /* restore rip of next */
"1: " /* next process start here */
"popl %%ebp
"
: "=m" (prev->thread.sp),"=m" (prev->thread.ip)
: "m" (next->thread.sp),"m" (next->thread.ip)
);
虽然是进程调度,但是实际处理的还是线程,将正在运行的线程数据保存,包括对栈底的保存(直接入栈),对栈顶的保存(存入对应的结构体),对ip的保存,这里区分了线程运行的状态,每一个运行过的线程都给了一个统一的标号点1,之后就是新运行线程环境的设置,对应设置其栈顶,使用ret的方式设置其ip位置,栈为空栈底等于栈底,并且这里每个线程都独享一个字符数组作为栈,每个栈底ebp对应在数组中的偏置位置是相同的。
- 小结
这里虽然模拟了单线程进程的调度,实际在多线程进程中的调度也大致相同,都是对线程运行的环境进行一个维护,相同进程的线程之间的调度可以在同一个进程控制块内进行调整,不同进程之间的线程调度则需要进行跨进程的调度,需要去维护多个进程控制块的信息,同进程线程共享进程资源,所以其与不同进程的线程的调度相比而言要复杂一些,但是其大致的流程也都类似。