《Linux内核分析》mooc第二周笔记

作者:30erli
原创作品转载请注明出处 + 《Linux内核分析》MOOC课程


理解运行mykernel,它提供了初始化好了的CPU从my_start_kernel开始执行,并提供了时钟中断机制,周期性执行my_time_handler中断处理程序,执行完中断后总是可以返回到my_start_kernel中断位置继续执行。中断保存现场和恢复现场的细节都处理好了,mykernel就是一个逻辑上的硬件平台。

要实现一个自己写的时间片轮转调度内核,以现在的水平来说,还是很困难的。现在能读懂别人写好的代码,也能增加认识。 本次实验一共只有4个进程,进程0启动,进程0>进程1>进程2>进程3>进程0循环周期调度切换。切换过程中伴随保护现场,恢复现场汇编操作。

实验效果:


部分关键代码:

//最大进程数
#define MAX_TASK_NUM4
//每个进程内核堆栈的大小
#define KERNEL_STACK_SIZE   1024*8

/* CPU-specific state of this task */
struct Thread {
unsigned long		ip;//用于暂存CPU的eip寄存器值
unsigned long		sp;//用于暂存CPU的esp寄存器值
};
//定义进程控制块
//进程在操作系统中都有一个户口,用于表示这个进程。
//这个户口操作系统被称为PCB(进程控制块),在本实验中具体实现是struct PCB数据结构
typedef struct PCB{
int pid;			//进程的ID
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);

extern tPCB task[MAX_TASK_NUM];//外部变量声明
extern tPCB * my_current_task;
extern volatile int my_need_sched;
volatile int time_count = 0;//tick计数

/*
 * 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)//tick计数1000次,并且当前进程调度状态非置位。
	{
		printk(KERN_NOTICE ">>>my_timer_handler here<<<\n");//打印中断提示
		my_need_sched = 1;//当前调度状态置位
	} 
	time_count ++ ;  //tick计数
	#endif
	return;  	
}
void my_schedule(void)
{
tPCB * next;//下一个进程控制块指针
tPCB * prev;//当前进程的前一个进程控制块指针

if(my_current_task == NULL 
    || my_current_task->next == NULL)//这段代码根本不会执行。当前进程控制块指针为空,或者下一个进程控制块指针为空,跳出my_schedule
{
	return;
}
printk(KERN_NOTICE ">>>my_schedule<<<\n");//打印调度提示
/* schedule */
next = my_current_task->next;//下一个进程控制块,暂存next
prev = my_current_task;//当前进程控制块,暂存prev
if(next->state == 0)/* -1 unrunnable, 0 runnable, >0 stopped *///如果下一个进程控制块为可一执行的。
{
	/* switch to next process */
	asm volatile(	
    	"pushl %%ebp\n\t" 	    /* save ebp *///ebp压栈
    	"movl %%esp,%0\n\t" 	/* save esp */
    	"movl %2,%%esp\n\t"     /* restore  esp */
    	"movl $1f,%1\n\t"       /* save eip */	
    	"pushl %3\n\t" 
    	"ret\n\t" 	            /* restore  eip */
    	"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)
	); 
	my_current_task = next; 
	printk(KERN_NOTICE ">>>switch %d to %d<<<\n",prev->pid,next->pid);   	
}
else				//初始化的时候进程控制块1,2,3的state==-1
{
    next->state = 0;		//将下一个进程控制块进程状态修改为可运行状态
    my_current_task = next;		//把下一个进程控制块指针赋值给当前进程指针
    printk(KERN_NOTICE ">>>1switch %d to %d<<<\n",prev->pid,next->pid);//打印进程切换提示
	/* switch to new process */
	asm volatile(	
    	"pushl %%ebp\n\t" 	    /* save ebp *///压栈ebp
    	"movl %%esp,%0\n\t" 	/* save esp *///将当前esp状态,存入上一个进程控制块
    	"movl %2,%%esp\n\t"     /* restore  esp *///
    	"movl %2,%%ebp\n\t"     /* restore  ebp *///movl %esp,%ebp   esp=thread.sp
    	"movl $1f,%1\n\t"       /* save eip */	//把标号1处的内存地址,存入上一个进程控制块。
    	"pushl %3\n\t" 				//把当前进程的eip压栈
    	"ret\n\t" 	            /* restore  eip *///执行当前的进程代码。
    	: "=m" (prev->thread.sp),"=m" (prev->thread.ip)
    	: "m" (next->thread.sp),"m" (next->thread.ip)
	);          
}   
return;	
}

tPCB task[MAX_TASK_NUM];//定义一个进程控制块结构体数组。
tPCB * my_current_task = NULL;//初始化当前进程指针为NULL
volatile int my_need_sched = 0;//

void my_process(void);


void __init my_start_kernel(void)//初始化CPU之后,首先执行该函数
{
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;//进程0的入口地址为my_process在内存中的地址,同时将入口地址保存在进程0的thread.ip中,为进程切换做准备
task[pid].thread.sp = (unsigned long)&task[pid].stack[KERNEL_STACK_SIZE-1];//取进程0内核栈顶地址为esp,并保存在进程0控制块的thread.sp中
task[pid].next = &task[pid];//进程控制块0的下一个进程控制块---暂时---赋值为它自己
/*fork more process */
for(i=1;i<MAX_TASK_NUM;i++)
{
    memcpy(&task[i],&task[0],sizeof(tPCB));//复制进程控制块0的所有值到进程控制块1,2...MAX_TASK_NUM。
    task[i].pid = i;			//修改进程控制块i的进程id为i
    task[i].state = -1;			//修改进程控制块i的进程状态为-1即不可运行状态
    task[i].thread.sp = (unsigned long)&task[i].stack[KERNEL_STACK_SIZE-1];//取进程i内核栈顶地址为esp,并保存进程i控制块的thread.sp中。
    task[i].next = task[i-1].next;		//
    task[i-1].next = &task[i];		//进程控制块0下一个指向进程控制块1然后依次1>2>3>0
}						
/* start process 0 by task[0] */
pid = 0;					//从进程0开始执行
my_current_task = &task[pid];		//把进程控制块0的地址,赋值给当前进程指针
asm volatile(
	"movl %1,%%esp\n\t" 	/* set task[pid].thread.sp to esp *///把进程控制块0的thread.sp(实际是进程控制块0内核堆栈的最高地址)赋值给esp寄存器,
	"pushl %1\n\t" 	        /* push ebp */			//把thread.sp压栈
	"pushl %0\n\t" 	        /* push task[pid].thread.ip *///把进程控制块0的程序入口地址压栈即myprocess在内存中的地址。
	"ret\n\t" 	            /* pop task[pid].thread.ip to eip *///把刚压入栈的程序入口地址即myprocess弹出到eip寄存器。下一步就进入my_process开始执行了。
	"popl %%ebp\n\t"
	: 
	: "c" (task[pid].thread.ip),"d" (task[pid].thread.sp)	/* input c or d mean %ecx/%edx*/
);
}   
void my_process(void)
{
int i = 0;
while(1)
{
    i++;
    if(i%300000000 == 0)//每10000000次i自加,执行一次下面代码段
    {
        printk(KERN_NOTICE "this is process %d -\n",my_current_task->pid);//打印当前进程id
        if(my_need_sched == 1)						//如果该进程调度状态置位(在时钟周期中断中进行置位),则复位状态,执行调度程序。
        {
            my_need_sched = 0;
    	    my_schedule();
        }
        printk(KERN_NOTICE "this is process %d +\n",my_current_task->pid);//打印调度之后,当前执行的进程的id
    }     
}
}
posted @ 2016-03-06 17:52  30erli  阅读(183)  评论(0)    收藏  举报