zoukankan      html  css  js  c++  java
  • 基于mykernel完成多进程的简单内核

    原创作品转载请注明出处 : https://github.com/mengning/linuxkernel/ 

    学号末三位:168   


    实验要求:完成一个简单的时间片轮转多道程序内核代码,参考代码见mykernel版本库

    实验环境:实验楼中的虚拟机。


     实验步骤:

    一、首先使用实验楼的虚拟机打开shell,按照如下步骤运行

    可以看到一个简单的kernel已经运行起来了,在一直不停的循环输出字符串。

    二、进入mykernel文件夹,查看mymain.c和myinterrupt.c这两个文件的内容

    由以上文件中注释可知,mymain.c文件用于执行进程,此处只是不停循环输出字符串,myinterrupt.c用来写时间片中断程序,此处只是不停的输出字符串。

    三、从mykernel版本库中下载对应的mymain.c和myinterrupt.c文件覆盖当前目录下的两个文件,并下载进程结构体定义文件mypcb.c

    使用maken重新编译后,重新运行程序查看输出。

    由输出内容可知,目前已实现进程切换的功能。


     代码分析:见注释

    一、mypcb.h文件

     1 /*
     2  *  linux/mykernel/mypcb.h
     3  *
     4  *  Kernel internal PCB types
     5  *
     6  *  Copyright (C) 2013  Mengning
     7  *
     8  */
     9 
    10 #define MAX_TASK_NUM        4
    11 #define KERNEL_STACK_SIZE   1024*2 # unsigned long
    12 /* CPU-specific state of this task */
    13 struct Thread {          //用于保存erp,esp
    14     unsigned long        ip;
    15     unsigned long        sp;
    16 };
    17 
    18 typedef struct PCB{
    19     int pid;
    20     volatile long state;    /* -1 unrunnable, 0 runnable, >0 stopped */
    21     unsigned long stack[KERNEL_STACK_SIZE];
    22     /* CPU-specific state of this task */
    23     struct Thread thread;
    24     unsigned long    task_entry;  //进程的入口
    25     struct PCB *next;
    26 }tPCB;
    27 
    28 void my_schedule(void);   //调度器

    该文件为进程块的描述文件,定义了进程的最大执行数为4,进程结构体中有进程号、进程状态、下一进程的地址等信息。

    二、myinterrupt.c文件

     1 /*
     2  *  linux/mykernel/myinterrupt.c
     3  *
     4  *  Kernel internal my_timer_handler
     5  *
     6  *  Copyright (C) 2013  Mengning
     7  *
     8  */
     9 #include <linux/types.h>
    10 #include <linux/string.h>
    11 #include <linux/ctype.h>
    12 #include <linux/tty.h>
    13 #include <linux/vmalloc.h>
    14 
    15 #include "mypcb.h"
    16 
    17 extern tPCB task[MAX_TASK_NUM];
    18 extern tPCB * my_current_task;
    19 extern volatile int my_need_sched;
    20 volatile int time_count = 0;
    21 
    22 /*
    23  * Called by timer interrupt.
    24  * it runs in the name of current running process,
    25  * so it use kernel stack of current running process
    26  */
    27 void my_timer_handler(void)
    28 {
    29 #if 1
    30     if(time_count%1000 == 0 && my_need_sched != 1)    //设置时间片大小,时间片用完时设置一下调度标志
    31     {
    32         printk(KERN_NOTICE ">>>my_timer_handler here<<<
    ");
    33         my_need_sched = 1;
    34     } 
    35     time_count ++ ;  
    36 #endif
    37     return;      
    38 }
    39 
    40 void my_schedule(void)
    41 {
    42     tPCB * next;
    43     tPCB * prev;
    44 
    45     if(my_current_task == NULL 
    46         || my_current_task->next == NULL)
    47     {
    48         return;
    49     }
    50     printk(KERN_NOTICE ">>>my_schedule<<<
    ");
    51     /* schedule */
    52     next = my_current_task->next;   //把当前进程的下一进程赋给next
    53     prev = my_current_task;
    54     if(next->state == 0)/* -1 unrunnable, 0 runnable, >0 stopped */
    55     {        
    56         my_current_task = next; 
    57         printk(KERN_NOTICE ">>>switch %d to %d<<<
    ",prev->pid,next->pid);  
    58         /* switch to next process */
    59         asm volatile(    
    60             "pushl %%ebp
    	"         /* save ebp */    //保存当前进程的ebp
    61             "movl %%esp,%0
    	"     /* save esp */     /把当前进程的esp赋给prev->thread.sp,保存
    62             "movl %2,%%esp
    	"     /* restore  esp */   //把下一进程的next->thread.sp赋给esp
    63             "movl $1f,%1
    	"       /* save eip */    //保存eip
    64             "pushl %3
    	"            //把下一进程的next->thread.ip进栈
    65             "ret
    	"                 /* restore  eip */
    66             "1:	"                  /* next process start here */
    67             "popl %%ebp
    	"
    68             : "=m" (prev->thread.sp),"=m" (prev->thread.ip)
    69             : "m" (next->thread.sp),"m" (next->thread.ip)
    70         ); 
    71     }  
    72     return;    
    73 }

    my_timer_handler()函数周期性的向CPU发出中段请求,my_schedule()函数用于完成进程的上下文切换,其中嵌入的汇编代码段,用于保存进程切换时的相关信息,可用于进程中断之后的恢复。

    三、mymain.c文件

     1 /*
     2  *  linux/mykernel/mymain.c
     3  *
     4  *  Kernel internal my_start_kernel
     5  *
     6  *  Copyright (C) 2013  Mengning
     7  *
     8  */
     9 #include <linux/types.h>
    10 #include <linux/string.h>
    11 #include <linux/ctype.h>
    12 #include <linux/tty.h>
    13 #include <linux/vmalloc.h>
    14 
    15 
    16 #include "mypcb.h"
    17 
    18 tPCB task[MAX_TASK_NUM];
    19 tPCB * my_current_task = NULL;
    20 volatile int my_need_sched = 0; //是否需要调度的标志
    21 
    22 void my_process(void);
    23 
    24 
    25 void __init my_start_kernel(void)
    26 {
    27     int pid = 0;  //初始化0号进程
    28     int i;
    29     /* Initialize process 0*/
    30     task[pid].pid = pid;
    31     task[pid].state = 0;/* -1 unrunnable, 0 runnable, >0 stopped */
    32     task[pid].task_entry = task[pid].thread.ip = (unsigned long)my_process;
    33     task[pid].thread.sp = (unsigned long)&task[pid].stack[KERNEL_STACK_SIZE-1];
    34     task[pid].next = &task[pid];
    35     /*fork more process */
    36     for(i=1;i<MAX_TASK_NUM;i++)    //创建更多的进程,每个进程有自己的堆栈,指向下一个进程
    37     {
    38         memcpy(&task[i],&task[0],sizeof(tPCB));
    39         task[i].pid = i;
    40     //*(&task[i].stack[KERNEL_STACK_SIZE-1] - 1) = (unsigned long)&task[i].stack[KERNEL_STACK_SIZE-1];
    41     task[i].thread.sp = (unsigned long)(&task[i].stack[KERNEL_STACK_SIZE-1]);
    42         task[i].next = task[i-1].next;
    43         task[i-1].next = &task[i];
    44     }
    45     /* start process 0 by task[0] */
    46     pid = 0;
    47     my_current_task = &task[pid];
    48     asm volatile(
    49         "movl %1,%%esp
    	"     /* set task[pid].thread.sp to esp */   //%1指task[pid].thread.sp
    50         "pushl %1
    	"             /* push ebp */
    51         "pushl %0
    	"             /* push task[pid].thread.ip */
    52         "ret
    	"                 /* pop task[pid].thread.ip to eip */    //ret之后0号进程正式启动
    53         : 
    54         : "c" (task[pid].thread.ip),"d" (task[pid].thread.sp)    /* input c or d mean %ecx/%edx*/
    55     );
    56 } 
    57 
    58 int i = 0;
    59 
    60 void my_process(void)
    61 {    
    62     while(1)
    63     {
    64         i++;
    65         if(i%10000000 == 0)      //循环10000000次,判断是否需要调度
    66         {
    67             printk(KERN_NOTICE "this is process %d -
    ",my_current_task->pid);
    68             if(my_need_sched == 1)
    69             {
    70                 my_need_sched = 0;
    71                 my_schedule();
    72             }
    73             printk(KERN_NOTICE "this is process %d +
    ",my_current_task->pid);
    74         }     
    75     }
    76 }

    mymain.c文件主要用于进程的初始化创建与启动,每个进程每执行10000000次且my_need_sched的值为1的时候就会调用my_schedule()函数,进行进程的切换。通过该种方法,实现了进程的时间片轮转调度。


    总结:操作系统一个非常重要的功能就是对进程的管理,对进程进行管理的算法有很多,包括时间片轮转调度算法、优先级调度算法、先来先服务调度算法等。本次实验实现的时间片轮转调度算法,主要的挑战是实现上下文的中断和进程的切换,处理方法在以上的mymain.c和myinterrupt.c文件中都有体现。

  • 相关阅读:
    实用机器学习 跟李沐学AI
    Explicitly drop temp table or let SQL Server handle it
    dotnettransformxdt and FatAntelope
    QQ拼音输入法 禁用模糊音
    (技术八卦)Java VS RoR
    Ruby on rails开发从头来(windows)(七)创建在线购物页面
    Ruby on rails开发从头来(windows)(十三)订单(Order)
    Ruby on rails开发从头来(windows)(十一)订单(Order)
    新员工自缢身亡,华为又站到了风口浪尖
    死亡汽油弹(Napalm Death)乐队的视频和来中国演出的消息
  • 原文地址:https://www.cnblogs.com/ustc-anmin/p/10505202.html
Copyright © 2011-2022 走看看