《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.程序运行的结果

   

二.实验总结

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

 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。