本文共 4234 字,大约阅读时间需要 14 分钟。
课程采用了的代码进行讲解
git地址 :
下面就是对mykernel代码的分析
大体上是在 start_kernel 中调用了 time_init,然后调用了my_start_kernel而在time_init中将my_timer_handler 作为处理时钟中断的函数,每1ms?调用一次?在my_start_kernel中建立了10个task结构体,并初始化,并形成了循环链表.然后将第一个task装载到进程中.装载的过程下面讲,然后就开始了A进程注意:每个进程的task结构体中的ip成员都是一样的,都是my_process函数的地址,所以不管是哪个进程,都是跑的my_process,虽然跑的指令一样,但是跑的数据不一样//此时内核所有的精力都在A进程中.除了偶尔接收一下时钟中断//设计的思路就在于在时钟中断的中断处理函数中.将A进程环境保存,装载B进程.然后B进程就在跑,内核所有的精力都在B进程,除了接收一下时钟中断.//所以,除了更底层的东西,我们需要关注的是1/装载一个进程(分为两种,一种是-1 unrunnable ,一种是0 runnable)2/保存一个进程环境(只有一种)我们可以在函数中看到,不管是装载进程,还是保存进程环境,都是用汇编写的所以我们要知道1/为什么要用汇编写这些东西 1/汇编写这些东西改变了3个寄存器,难道用C语言不能改变吗?可以,用fork和exec 1/之前装载一个进程是用fork和exec族,为什么不用fork和exec? 1/fork和exec是怎么实现的?2/内嵌汇编怎么写?请查阅
//装载一个进程 -1 unrunnable1/设置%esp寄存器2/设置%ebp寄存器3/设置%eip寄存器//而在第0个进程中,是这么做的 pid = 0; asm volatile( "movl %1,%%esp\n\t" /* set task[pid].thread.sp to esp */ "pushl %1\n\t" /* push ebp */ "pushl %0\n\t" /* push task[pid].thread.ip */ "ret\n\t" /* pop task[pid].thread.ip to eip */ "popl %%ebp\n\t" : : "c" (task[pid].thread.ip),"d" (task[pid].thread.sp) /* input c or d mean %ecx/%edx*/ ); //一步一步分析movl %1,%esp//这个是将task[pid].thread.ip stack[4095]的地址放入 esp ,因为数组是往高处增长的,所以高处的地址更大.pushl %1//压栈,将esp的值压栈到stack[4094]位置//其实一般这个位置应该是 push %ebp ,但是写了push %1 ,所以就代表 %1 的值就是%ebp的值,这个是老的ebp的值pushl %0//压栈,将task[pid].thread.ip 即 my_process 的地址 压栈到stack[4093]位置ret//弹栈,将my_process的地址转入eip,并esp+4,此时esp指向stack[4094]下一步就应该执行my_process然而popl %ebp 这个什么时候做呢???//这个永远不会做//问题来了,既然%esp,%eip都设置了,那么%ebp呢?进程0中没有设置%ebp,是不是这样子?mykernel/myinterrupt.c中也有一个加载新进程的例子 /* switch to new process */ asm volatile( //"pushl %%ebp\n\t" /* save ebp */ //"movl %%esp,%0\n\t" /* save esp */ //新进程 "movl %2,%%esp\n\t" /* restore esp */ "movl %2,%%ebp\n\t" /* restore ebp */ //"movl $1f,%1\n\t" /* save eip */ "pushl %3\n\t" "ret\n\t" /* restore eip */ : "=m" (prev->thread.sp),"=m" (prev->thread.ip) : "m" (next->thread.sp),"m" (next->thread.ip) );我把无关的注释掉了,movl %2,%espmovl %2,%ebp看来%esp和%ebp是一样的,都是next->thread.sp,都是stack[4095]pushl %3ret修改了%esp//可以看出来,这个mykernel/myinterrupt.c 是一个完整的修改 3个寄存器 的例子.但是之前的mymain.c没有修改%ebp
asm volatile( //"pushl %%ebp\n\t" /* save ebp */ //"movl %%esp,%0\n\t" /* save esp */ //恢复旧进程 "movl %2,%%esp\n\t" /* restore esp */ //"movl $1f,%1\n\t" /* save eip */ "pushl %3\n\t" "ret\n\t" /* restore eip */ "1:\t" /* next process start here */ "popl %%ebp\n\t" : "=m" (prev->thread.sp),"=m" (prev->thread.ip) : "m" (next->thread.sp),"m" (next->thread.ip) );//这个是mykernel/myinterrupt.c中的例子,我把无关的注释掉了可以看到,这里面并没有修改%ebp,为什么呢?//这里虽然在栈顶,可是没有popl %ebp 得到 %ebp//所以下次调用函数的时候 或者 ret的时候第一句就要执行 pushl %ebp ,那么这个ebp 是多少呢? 是上一个环境的ebp????因为后面可以看到,对于runnable 进程,ebp被压栈了,所以ebp一直都在栈里面而且还多了一个 1: 和 popl %%ebp 为什么呢?1: 好像是个标号.
1/保存%esp寄存器2/保存%ebp寄存器3/保存%eip寄存器mykernel/myinterrupt.c中也有两个加载新进程的例子,本质其实一样,我把无关的注释了 /* switch to new process */ asm volatile( "pushl %%ebp\n\t" /* save ebp */ "movl %%esp,%0\n\t" /* save esp */ //新进程 //"movl %2,%%esp\n\t" /* restore esp */ //"movl %2,%%ebp\n\t" /* restore ebp */ "movl $1f,%1\n\t" /* save eip */ //"pushl %3\n\t" //"ret\n\t" /* restore eip */ : "=m" (prev->thread.sp),"=m" (prev->thread.ip) : "m" (next->thread.sp),"m" (next->thread.ip) );//可见ebp被堆栈了,且放到了栈顶,所以下次恢复的时候,直接popl %ebp 就可以了//esp 和eip被保存到task结构体里面了.pushl %%ebpmovl %%esp,%0movl $1f,%1 //问题1:为什将 1f(即当前的%eip),这个数值 1f 保存到 prev->thread.ip 中?这个数值代表什么?
//在系统中,中断是调度的前提条件,如果屏蔽了中断,那么就不会再有调度可见,调度时由定时器中断的中断处理函数做的.由此可见,该中断的优先级一定不能低,如果低了的话,被禁止了,那么就没有调度了
转载地址:http://mtigi.baihongyu.com/