一个简单的时间片轮转多道程序内核代码分析 (学号后三位418)

Posted xlyyz

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一个简单的时间片轮转多道程序内核代码分析 (学号后三位418)相关的知识,希望对你有一定的参考价值。

一、基于mykernel的基本Linux内核分析

  1.我们按照老师在github上给出的步骤在实验楼上启动最高小内核,可以看到如下现象

技术图片

  在窗口中我们可以看到一个内核以及运行起来了,比较简单的内核,只时不停的输出一些字符串,>>>>>>my_time_handler here<<<<<<<和my_start_kernel here和一些计数。这时因为我们并没有加入其他的代码,再次基础上我们可以加入我们主机要实现的功能。

 在myintrrupt.c以及mymain.c中的核心代码如下图,只有简单打印一些字符串以及计数的功能

技术图片

技术图片

  在myinterrupt.c中我们可以看到一个会被时钟终端周期调用的函数my_timer_handler,在这个函数中,会周期性输出>>>>my_timer_handler here<<<的字符串。

通过这个实验我们可以知道,mykernel系统启动后会做两件事:

    1)调用my_start_kernel函数

    2)  周期性的调用my_timer_handler函数

  观察内核启动过程可以发现,当执行myinterrupt后返回mymain时,终端输出的累计循环次数是连续的没有重置,说明CPU和内核代码实现了保存现场和恢复现场的功能,会将一些重要的寄存器,如eip、ebp、esp等寄存器的内容将会被保存到堆栈中,等待切换回来的时候继续执行。为了实现一个按时间片轮转多道程序的内核,我们需要在其基础上进行修改。

二、按时间片轮转多道程序的实现

  我们需要修改mymain.c和myinterrupt.c,先来分析有关的数据结构代码:

  

 1 /***mypcb.h***/
 2 #define MAX_TASK_NUM   4           //定义最大任务数
 3 #define KERNEL_STACK_SIZE   1024*2  //堆栈大小
 4 
 5 strcuct  Thread{
 6     unsigned lone        ip;      //保存执行代码片段所在的实际物理内存地址
 7     unsigned lone        sp;      //存放栈顶地址
 8 };
 9 
10 typedef strucut PCB{        //进程控制块
11     int pid;
12     volatile long state;
13     unsigned long stack[KERNEL_STACK_SIZE];
14     struct Thread thread;
15     unsigned long   task_entry;
16     struct PCB *next;
17 }tPCB;
18 
19 void my_schedule(void);
 1 /*****mymain.c*****/
 2 #include <linux/types.h>
 3 #include <linux/string.h>
 4 #include <linux/ctype.h>
 5 #include <linux/tty.h>
 6 #include <linux/vmalloc.h>
 7 
 8 
 9 #include "mypcb.h"
10 
11 tPCB task[MAX_TASK_NUM];
12 tPCB * my_current_task = NULL;
13 volatile int my_need_sched = 0;
14 
15 void my_process(void);
16 
17 
18 void __init my_start_kernel(void)
19 {
20     int pid = 0;
21     int i;
22     /* Initialize process 0*/
23     task[pid].pid = pid;
24     task[pid].state = 0;
25     /* -1 unrunnable, 0 runnable, >0 stopped */
26     task[pid].task_entry = task[pid].thread.ip = (unsigned long)my_process;
27     task[pid].thread.sp = (unsigned long)&task[pid].stack[KERNEL_STACK_SIZE-1];
28     task[pid].next = &task[pid];
29     /*fork more process */
30     for(i=1;i<MAX_TASK_NUM;i++)
31     {
32         memcpy(&task[i],&task[0],sizeof(tPCB));
33         task[i].pid = i;
34         task[i].thread.sp = (unsigned long)&task[i].stack[KERNEL_STACK_SIZE-1];
35     *(task[i].thread.sp - 1) = task[i].thread.sp;
36     task[i].thread.sp -= 1;
37         task[i].next = task[i-1].next;
38         task[i-1].next = &task[i];
39     }
40     /* start process 0 by task[0] */
41     pid = 0;
42     my_current_task = &task[pid];
43     asm volatile(
44         "movl %1,%%esp
	"     
45 /* set task[pid].thread.sp to esp */
46         "pushl %1
	"        
47   /* push ebp */
48         "pushl %0
	"         
49  /* push task[pid].thread.ip */
50         "ret
	"               
51 /* pop task[pid].thread.ip to eip */
52         "popl %%ebp
	"
53         : 
54         : "c" (task[pid].thread.ip),"d" (task[pid].thread.sp)   
55 /* input c or d mean %ecx/%edx*/
56     );
57 }   
58 void my_process(void)
59 {
60     int i = 0;
61     while(1)
62     {
63         i++;
64         if(i%10000000 == 0)
65         {
66             printk(KERN_NOTICE "this is process %d -
",my_current_task->pid);
67             if(my_need_sched == 1)
68             {
69                 my_need_sched = 0;
70                 my_schedule();
71             }
72             printk(KERN_NOTICE "this is process %d +
",my_current_task->pid);
73         }     
74     }
75 }

  阅读以上代码,这函数 my_start_kernel 是系统启动后,最先调用的函数,在这个函数里完成了0号进程的初始化和启动,并创建了其它的进程PCB,以方便后面的调度。在系统里,每个进程的函数代码都是一样的,即 my_process 函数,my_process 在执行的时候,会打印出当前进程的 id,从而使得我们能够看到当前哪个进程正在执行

 1 /*****myinterrupt*****/
 2 #include <linux/types.h>
 3 #include <linux/string.h>
 4 #include <linux/ctype.h>
 5 #include <linux/tty.h>
 6 #include <linux/vmalloc.h>
 7 
 8 #include "mypcb.h"
 9 
10 extern tPCB task[MAX_TASK_NUM];
11 extern tPCB * my_current_task;
12 extern volatile int my_need_sched;
13 volatile int time_count = 0;
14 
15 /*
16  * Called by timer interrupt.
17  * it runs in the name of current running process,
18  * so it use kernel stack of current running process
19  */
20 void my_timer_handler(void)
21 {
22 #if 1
23     if(time_count%1000 == 0 && my_need_sched != 1)   //计数一千次且需要调度标志位为1
24     {
25         printk(KERN_NOTICE ">>>my_timer_handler here<<<
");
26         my_need_sched = 1;
27     } 
28     time_count ++ ;  
29 #endif
30     return;     
31 }
32 
33 void my_schedule(void)
34 {
35     tPCB * next;
36     tPCB * prev;
37 
38     if(my_current_task == NULL 
39         || my_current_task->next == NULL)
40     {
41         return;
42     }
43     printk(KERN_NOTICE ">>>my_schedule<<<
");
44     /* schedule */
45     next = my_current_task->next;
46     prev = my_current_task;
47     if(next->state == 0)
48 /* -1 unrunnable, 0 runnable, >0 stopped */
49     {        
50         my_current_task = next; 
51         printk(KERN_NOTICE ">>>switch %d to %d<<<
",prev->pid,next->pid);  
52         /* switch to next process */
53         asm volatile(         
54             "pushl %%ebp
	"       /* save ebp */
55             "movl %%esp,%0
	"     /* save esp */
56             "movl %2,%%esp
	"     /* restore  esp */
57             "movl $1f,%1
	"       /* save eip */  
58             "pushl %3
	" 
59             "ret
	"               /* restore  eip */
60             "1:	"                  /* next process start here */
61             "popl %%ebp
	"
62             : "=m" (prev->thread.sp),"=m" (prev->thread.ip)
63             : "m" (next->thread.sp),"m" (next->thread.ip)
64         ); 
65     }  
66     return; 
67 }

  

技术图片

 

my_timer_handler函数会被内核周期性的调用,就去将全局变量my_need_sched的值修改为。通知正在执行调用程序my_schedule。在my_schedule函数中,完成进程的切换。进程的切换分为两种情况,一种情况时下一个进程没有被调度过,另一种情况时下一个进程被调度过,可以通过下一个进程的state知道其状态。进程切换依然时通过内联会变代码实现,就是保存酒进程的eip和相关堆栈,将新进程的eip和堆栈的值存入对应的寄存器中。

三、总结

  通过本次实验操作,成功实现一个简单的时间片轮转的多道程序,通过一个精简的操作系统内核,完成了一个简单的功能的操作系统。操作系统的进程调度大致为以下步骤:

   1)cpu通过总线根据cs:ip的值从内存的代码段读取一条指令,读取的指令进入缓冲器

   2)ip=ip+所指令的长度,从而指向下一条指令;

   3)转向1重复执行

以上是关于一个简单的时间片轮转多道程序内核代码分析 (学号后三位418)的主要内容,如果未能解决你的问题,请参考以下文章

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

一个简单的时间片轮转内核代码的分析(课程作业)

完成一个简单的时间片轮转多道程序内核代码

一个简单的时间片轮转多道程序内核代码分析 (学号后三位418)

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

linux内核分析作业:操作系统是如何工作的进行:完成一个简单的时间片轮转多道程序内核代码