zoukankan      html  css  js  c++  java
  • 基于mykernel的时间片轮转调度

    学号: 363

    原创作品,转载请注明出处。
    本实验资源来源: https://github.com/mengning/linuxkernel/

    一、 实验环境配置

    本次实验在实验楼完成:

    在实验楼的终端下输入下面命令:

    cd LinuxKernel/linux-3.9.4
    rm -rf mykernel
    patch -p1 < ../mykernel_for_linux3.9.4sc.patch
    make allnoconfig
    make 
    qemu -kernel arch/x86/boot/bzImage

    可查看运行结果

     关闭qemu窗口,进入mykernel文件夹,可以查看mymain.c和myinterrupt.c文件。

    mymain.c的代码不断循环的去执行,周期性的产生时钟中断信号,去执行myinterrupt.c的代码。

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

    将mymain.c,myinterrupt.c,mypcb.h三个文件复制替换到mykernel文件夹下。

    运行如下:

    可以看到进程1切换到了进程2。

    三、时间片轮转多道程序的代码分析

    mypcb.h

    /*
     *  linux/mykernel/mypcb.h
     *
     *  Kernel internal PCB types
     *
     *  Copyright (C) 2013  Mengning
     *
     */
    
    #define MAX_TASK_NUM        4
    #define KERNEL_STACK_SIZE   1024*2 # unsigned long
    /* CPU-specific state of this task */
    struct Thread {
        unsigned long        ip;
        unsigned long        sp;
    };
    
    typedef struct PCB{
        int pid;
        volatile long state;    /* -1 unrunnable, 0 runnable, >0 stopped */
        unsigned long stack[KERNEL_STACK_SIZE];
        /* CPU-specific state of this task */
        struct Thread thread;
        unsigned long    task_entry;
        struct PCB *next;
    }tPCB;
    
    void my_schedule(void);

    可以看到最大进程数定义为四个,程序控制块PCB中定义了pid,状态statue,线程thread,进程入口函数task_entry等.

    mymain.c文件

    /*
     *  linux/mykernel/mymain.c
     *
     *  Kernel internal my_start_kernel
     *
     *  Copyright (C) 2013  Mengning
     *
     */
    #include <linux/types.h>
    #include <linux/string.h>
    #include <linux/ctype.h>
    #include <linux/tty.h>
    #include <linux/vmalloc.h>
    
    
    #include "mypcb.h"
    
    tPCB task[MAX_TASK_NUM];
    tPCB * my_current_task = NULL;
    volatile int my_need_sched = 0;
    
    void my_process(void);
    
    
    void __init my_start_kernel(void)
    {
        int pid = 0;
        int i;
        /* Initialize process 0*/
        task[pid].pid = pid;
        task[pid].state = 0;/* -1 unrunnable, 0 runnable, >0 stopped */
        task[pid].task_entry = task[pid].thread.ip = (unsigned long)my_process;
        task[pid].thread.sp = (unsigned long)&task[pid].stack[KERNEL_STACK_SIZE-1];
        task[pid].next = &task[pid];
        /*fork more process */
        for(i=1;i<MAX_TASK_NUM;i++)
        {
            memcpy(&task[i],&task[0],sizeof(tPCB));
            task[i].pid = i;
        //*(&task[i].stack[KERNEL_STACK_SIZE-1] - 1) = (unsigned long)&task[i].stack[KERNEL_STACK_SIZE-1];
        task[i].thread.sp = (unsigned long)(&task[i].stack[KERNEL_STACK_SIZE-1]);
            task[i].next = task[i-1].next;
            task[i-1].next = &task[i];
        }
        /* start process 0 by task[0] */
        pid = 0;
        my_current_task = &task[pid];
        asm volatile(
            "movl %1,%%esp
    	"     /* set task[pid].thread.sp to esp */
            "pushl %1
    	"             /* push ebp */
            "pushl %0
    	"             /* push task[pid].thread.ip */
            "ret
    	"                 /* pop task[pid].thread.ip to eip */
            : 
            : "c" (task[pid].thread.ip),"d" (task[pid].thread.sp)    /* input c or d mean %ecx/%edx*/
        );
    } 
    
    int i = 0;
    
    void my_process(void)
    {    
        while(1)
        {
            i++;
            if(i%10000000 == 0)
            {
                printk(KERN_NOTICE "this is process %d -
    ",my_current_task->pid);
                if(my_need_sched == 1)
                {
                    my_need_sched = 0;
                    my_schedule();
                }
                printk(KERN_NOTICE "this is process %d +
    ",my_current_task->pid);
            }     
        }
    }

    在这个文件中void __init my_start_kernel(void)这个函数fork了4个新进程,把新fork的进程加入到进程链表在这个文件中。
    汇编过程如下:
    (1)将0号进程的esp的值赋给ESP寄存器
    (2)将0号进程的esp的值压栈(此时堆栈状态为进程0的堆栈)
    (3)将0号进程的eip的值压栈
    (4)通过ret指令,让栈顶的eip的值出栈到EIP寄存器中(间接改变EIP寄存器的值),完成进程0的启动

    myinterupt.c

    /*
     *  linux/mykernel/myinterrupt.c
     *
     *  Kernel internal my_timer_handler
     *
     *  Copyright (C) 2013  Mengning
     *
     */
    #include <linux/types.h>
    #include <linux/string.h>
    #include <linux/ctype.h>
    #include <linux/tty.h>
    #include <linux/vmalloc.h>
    
    #include "mypcb.h"
    
    extern tPCB task[MAX_TASK_NUM];
    extern tPCB * my_current_task;
    extern volatile int my_need_sched;
    volatile int time_count = 0;
    
    /*
     * Called by timer interrupt.
     * it runs in the name of current running process,
     * so it use kernel stack of current running process
     */
    void my_timer_handler(void)
    {
    #if 1
        if(time_count%1000 == 0 && my_need_sched != 1)
        {
            printk(KERN_NOTICE ">>>my_timer_handler here<<<
    ");
            my_need_sched = 1;
        } 
        time_count ++ ;  
    #endif
        return;      
    }
    
    void my_schedule(void)
    {
        tPCB * next;
        tPCB * prev;
    
        if(my_current_task == NULL 
            || my_current_task->next == NULL)
        {
            return;
        }
        printk(KERN_NOTICE ">>>my_schedule<<<
    ");
        /* schedule */
        next = my_current_task->next;
        prev = my_current_task;
        if(next->state == 0)/* -1 unrunnable, 0 runnable, >0 stopped */
        {        
            my_current_task = next; 
            printk(KERN_NOTICE ">>>switch %d to %d<<<
    ",prev->pid,next->pid);  
            /* switch to next process */
            asm volatile(    
                "pushl %%ebp
    	"         /* save ebp */
                "movl %%esp,%0
    	"     /* save esp */
                "movl %2,%%esp
    	"     /* restore  esp */
                "movl $1f,%1
    	"       /* save eip */    
                "pushl %3
    	" 
                "ret
    	"                 /* restore  eip */
                "1:	"                  /* next process start here */
                "popl %%ebp
    	"
                : "=m" (prev->thread.sp),"=m" (prev->thread.ip)
                : "m" (next->thread.sp),"m" (next->thread.ip)
            ); 
        }  
        return;    
    }

    通过my_time_handler()函数定时地不断向cpu发出中断,从而实现了时间片轮转。每调用1000次,就去将全局变量my_need_sched的值修改为1,通知正在执行的进程执行调度程序my_schedule。从而在my_schedule函数中完成进程的不断切换。

    四、总结


    (1)进程和中断在操作系统是是非常重要的两个部分,需要熟练掌握。
    (2)EIP寄存器储存着当前执行的代码,可以通过更改EIP寄存器的值来更改当前执行的代码,从而实现进程切换。出于安全考虑,EIP寄存器的值不能被直接改变,但可以通过压栈+ret指令来间接改变。
    (3)进程在执行过程中,当时间片用完之后需要进程切换时,需要保存当前的执行上下文环境,下次被调度的时候,需要回复进程的上下文环境。

  • 相关阅读:
    html 上传图片前预览
    php获取当月天数及当月第一天及最后一天、上月第一天及最后一天实现方法
    php 计算 pdf文件页数
    php 获取半年内每个月的订单数量, 总价, 月份
    php 获取两个数组之间不同的值
    小程序支付功能
    关于nginx的Job for nginx.service failed because the control process exited with error code.错误
    linux 安装 Apollo
    MongoDB待续。。。
    ABP vNext...待续
  • 原文地址:https://www.cnblogs.com/xiguas/p/10519273.html
Copyright © 2011-2022 走看看