《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<<<\n");

        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\n\t"    //保存当前进程的栈基址指针 ebp

        "movl  %%esp,%0\n\t" //保存当前进程的栈顶指针 esp

        "movl %2,%%esp\n\t"  //回复下一个进程的栈顶指针 esp

        "movl $1f,%1\n\t"    //将当前进程的下一个指令地址保存到thread.ip中

        "pushl %3\n\t"       //将下一个进程的指令执行地址入栈

        "ret\n\t"            //ip出栈,

        "1:\t"   //next process start here

        "popl %%ebp\n\t"  //构建下一个进程的堆栈

        :"=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\n\t"     /* set task[pid].thread.sp to esp */

        "pushl %1\n\t"             /* push ebp */

        "pushl %0\n\t"             /* push task[pid].thread.ip */

        "ret\n\t"                 /* pop task[pid].thread.ip to eip */

        "popl %%ebp\n\t"

        : 

        : "c" (task[pid].thread.ip),"d" (task[pid].thread.sp)    /* input c or d mean %ecx/%edx*/

    );

}   

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

4.程序运行的结果

   

二.实验总结

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

 

你可能感兴趣的:(linux)