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中的步骤,在自己虚拟机中一点点做的,最后成功做出来了,截图如下:
文件夹内容

运行截图1

运行截图2

运行截图3

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

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

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

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

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

**闫佳歆 原创作品转载请注明出处 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000 **

posted @ 2016-03-06 00:32  20135202闫佳歆  阅读(255)  评论(2编辑  收藏  举报