所用代码为课程配套git库中下载得到的。
/*出自mymain.c*/
/* start process 0 by task[0] */
pid = 0;
my_current_task = &task[pid];
asm volatile(
"movl %1,%%esp\n\t" /* 将进程的sp赋给esp寄存器 */
"pushl %1\n\t" /* ebp入栈:因为在这里栈为空,esp=ebp,所以push的%1就是esp就是ebp。*/
"pushl %0\n\t" /* 进程入口ip入栈 */
"ret\n\t" /* 把进程入口ip赋给eip,即从这之后0号进程启动。*/
"popl %%ebp\n\t"
:
: "c" (task[pid].thread.ip),"d" (task[pid].thread.sp) /* input c or d mean %ecx/%edx*/
);
进程的切换有两种:
/*出自myinterrupt.c*/
//两个正在运行的进程之间做进程上下文切换
if(next->state == 0)
/* state值的含义:-1表示没有执行,0表示正在执行,>0表示停止,这里为0,即进程正在执行 */
{
/* 以下是进程切换关键代码 */
asm volatile(
"pushl %%ebp\n\t" /* 把当前进程的ebp保存*/
"movl %%esp,%0\n\t" /* 把当前进程的esp赋值到sp中保存下来*/
"movl %2,%%esp\n\t" /* 把下一个进程的sp放到esp中*/
"movl $1f,%1\n\t" /* 把eip保存起来,$1f指接下来的标号1:的位置*/
"pushl %3\n\t" /*把下一个进程的eip保存起来*/
"ret\n\t" /* 还原eip */
"1:\t" /* 标号1,下一进程从此开始 */
"popl %%ebp\n\t"
: "=m" (prev->thread.sp),"=m" (prev->thread.ip)
: "m" (next->thread.sp),"m" (next->thread.ip)
);
my_current_task = next;
printk(KERN_NOTICE ">>>switch %d to %d<<<\n",prev->pid,next->pid);
}
/*出自myinterrupt.c*/
/*这段代码是当进程从未执行过时,所执行的动作,即启动一个进程
next->state = 0; /* 首先要把进程置为运行时状态,作为当前正在执行的进程 */
my_current_task = next;
printk(KERN_NOTICE ">>>switch %d to %d<<<\n",prev->pid,next->pid);
/* 进程切换时的提示,从当前进程切换至下一进程*/
asm volatile( //混合编程
"pushl %%ebp\n\t" /* 保存ebp */
"movl %%esp,%0\n\t" /* 保存esp */
"movl %2,%%esp\n\t" /* 将下一进程的sp存入esp */
"movl %2,%%ebp\n\t" /* 将下一进程的bp存入ebp,因为栈空,所以esp和ebp指向同一位置 */
"movl $1f,%1\n\t" /* 保存eip */
"pushl %3\n\t" /*保存当前进程入口 */
"ret\n\t" /* 还原eip */
: "=m" (prev->thread.sp),"=m" (prev->thread.ip)
: "m" (next->thread.sp),"m" (next->thread.ip)
);
这个实验我是根据github中的步骤,在自己虚拟机中一点点做的,最后成功做出来了,截图如下:
就像计算机有三件法宝一样,操作系统也有“两把剑”,分别是中断上下文和进程上下文的切换
有了中断后,就有了多道程序设计,每个程序有自己的执行流,中断发生时,cpu把当前的eip等压入内核堆栈中,然后把eip指向中断处理程序的入口。
在模拟操作系统的代码里可以看出,mypcb.h中定义了结构体,用于存放sp和ip,以及pcb块,每个pcb块就是一个进程。
然后在mymain.c中,先创建了几个进程,具体创建过程见上。
然后设立一个循环,比如循环一百万次再来判定是否需要调度,这是建立了一个主动的调度机制,对应的是这个函数:
void my_process(void)
{
int i = 0;
while(1)
{
i++;
if(i%10000000 == 0)
{
printk(KERN_NOTICE "this is process %d -\n",my_current_task->pid);
if(my_need_sched == 1)//执行一千万次才判断是否需要调度。有一个主动调度的机制。
{
my_need_sched = 0;
my_schedule();
}
printk(KERN_NOTICE "this is process %d +\n",my_current_task->pid);
}
}
}
有调度就要有进程的的切换,切换也在上面说过了。
因为是时间片轮转,所以需要设立一个时间片,当时间片用尽时设立调度标志,如下:
void my_timer_handler(void)
{
#if 1
//设置时间片的大小,时间片用完时设置一下调度标志。
if(time_count%1000 == 0 && my_need_sched != 1)
{
printk(KERN_NOTICE ">>>my_timer_handler here<<<\n");
my_need_sched = 1;
}
time_count ++ ;
#endif
return;
}
总体思路就是时间片用尽时设置调度标志,然后判断是否需要调度用一个无限循环控制,需要调度了就切换进程。
最后附上这周的=学习笔记