20135202闫佳歆--week2 一个简单的时间片轮转多道程序内核代码及分析

一个简单的时间片轮转多道程序内核代码及分析

所用代码为课程配套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*/
    );

二、进程的切换

进程的切换有两种:

1.下一个进程next->state == 0 即正在执行时

/*出自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);      
}

2.进程是一个新进程,还从未执行过

/*出自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中的步骤,在自己虚拟机中一点点做的,最后成功做出来了,截图如下:
20135202闫佳歆--week2 一个简单的时间片轮转多道程序内核代码及分析_第1张图片

20135202闫佳歆--week2 一个简单的时间片轮转多道程序内核代码及分析_第2张图片

四、操作系统是如何工作的

就像计算机有三件法宝一样,操作系统也有“两把剑”,分别是中断上下文进程上下文的切换

有了中断后,就有了多道程序设计,每个程序有自己的执行流,中断发生时,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;     
}

总体思路就是时间片用尽时设置调度标志,然后判断是否需要调度用一个无限循环控制,需要调度了就切换进程。

最后附上这周的=学习笔记

你可能感兴趣的:(20135202闫佳歆--week2 一个简单的时间片轮转多道程序内核代码及分析)