zoukankan      html  css  js  c++  java
  • 《Linux内核分析》 week2作业-时间片轮转

    一.基于时间片轮转调度代码的解读

        代码结构主要由三个文件组成: 

        1.mypcb.h

        2.myinterrupt.c

        3.mymain.c

    1.进程控制块(mypcb.h)

    /* CPU-specific state of this task */
    struct Thread{
       unsigned long ip;  //eip,程序入口地址
       unsigned long sp;  //堆栈esp栈顶地址
    };
    
    typedef struct PCB{
       int pid;  //进程pid号
       volatile long state; //进程运行状态,-1 unrunnable,0 runnable,>0 stopped
       char stack[KERNEL_STACK_SIZE]; //进程的栈空间
       /* CPU-specific state of this task */
       struct Thread thread;  //CPU的相关状态
       unsigned long task_entry; //进程运行对应的函数
       struct PCB *next;  //下一个进程块地址
    }tPCB;
    
    void my_schedule(void);

    这里进程控制块(PCB)是采用链表的形式链接起来的。

    2.进程的切换(myinterrupt.c)

    extern tPCB task[MAX_TASK_NUM]; //进程控制块数组
    extern tPCB *my_current_task;  //当前对应的进程控制块
    extern volatile int my_need_sched; //标志字段,来表示是否需要对进程进行调度
    
    //时钟中断,周期性调用这个函数
    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;
    }

     接下来是进程上下文切换最关键的代码(my_schedule函数),采用的是内嵌汇编代码

     当下一个进程的状态是正在运行时,则

    if(next->state==0){// -1 unrunnable,0 runnable,>0 stopped
        /*switch to next process */
        asm volatile(
            "pushl %%ebp
    	"    //保存当前进程的栈基址指针 ebp
            "movl  %%esp,%0
    	" //保存当前进程的栈顶指针 esp
            "movl %2,%%esp
    	"  //回复下一个进程的栈顶指针 esp
            "movl $1f,%1
    	"    //将当前进程的下一个指令地址保存到thread.ip中
            "pushl %3
    	"       //将下一个进程的指令执行地址入栈
            "ret
    	"            //ip出栈,
            "1:	"   //next process start here
            "popl %%ebp
    	"  //构建下一个进程的堆栈
            :"=m"(prev->thread.sp),"=m"(prev->thread.ip)
            :"m"(next->thread.sp),"m"(next->thread.ip)
        );
    } 

    若下一个进程是新的进程,还没有执行过,基本方式与上面差不多,也是基于内嵌汇编方式实现上下文切换。

    3.内核代码运行(mymain.c)

     内核从my_start_kernel开始执行,my_start_kernel主要是进行进程控制块的初始化,同时启动0号进程。

     

    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].state = -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 */
            "popl %%ebp
    	"
            : 
            : "c" (task[pid].thread.ip),"d" (task[pid].thread.sp)    /* input c or d mean %ecx/%edx*/
        );
    }   

    然后0号进程开始执行my_process函数。

    4.程序运行的结果

       

    二.实验总结

      通过完成这个简单的时间片轮转多道程序的实验,让我更加深刻的理解了进程上下文切换的原理,以及内核启动的初始化过程。

  • 相关阅读:
    windows wmi
    python编码规范
    gogs安装
    mariadb-5.5安装
    python kafka
    delimiter关键字
    phpstorm设置背景图片
    linux 下mysql 关闭 启动
    通过下载git包来安装git
    git clone 某个链接时候报错Initialized empty Git repository in 不能克隆
  • 原文地址:https://www.cnblogs.com/sixue/p/4430277.html
Copyright © 2011-2022 走看看