2019-2020-1 20199313《Linux内核原理与分析》第三周作业
第三周学习——操作系统是如何运作的
- 问题描述:
- 经过上一周的学习,我们已经初步了解了C语言经由汇编程序汇编之后的代码将如何在存储程序计算机工作模型上逐步运行,并且学习了默认的小妖精( 计算机的堆栈模型),以及各种寄存器和指针是如何相互配合,完美的形成堆栈的。
- 本周我们将进一步学习计算机操作系统的核心工作机制。
- 进一步分析函数调用堆栈机制,以及C代码内嵌汇编的写法。
- 以及,学会使用mykernel的基础上编写一个简单的内核,
一、函数调用堆栈
在上周我们已经对其进行了一个系统的分析,这周在学习新课之前,再次复习一下。
堆栈诞生
在最简单的程序运行过程中,指令只是一条接着一条运行到底。但是,当遇到一些情况下,需要暂时停止运行主干程序,而需要调用某个函数进行一定的运算时,当前各个寄存器中的数据又十分重要不能随便丢弃,需要放入存储器暂时储存,当再次需要用到时也需要快速调出,于是,栈便应运而生。
复习
栈同样是寄存器的一部分,他的大致示意图如下:
pushl 指令和popl 指令
-
入栈指令pushl
将某一数据压入堆栈,将栈顶指针移向下一个空位,并将%eax中存放的数据压入堆栈例如 pushl %eas 形象地表示为:
subl $4, $esp movl %eax, (%esp)
-
出栈指令popl
将堆栈中位于最顶部的数据进行出栈操作,将位于栈顶的数据赋值给%eax,并将栈顶指针移向上一个位置。例如 popl %eax 形象的表示为:
movl (%esp), %eax add $4, $esp
call 指令
-
函数调用指令
对于程序员而言,我们在C语言经常用到此类操作,而这条指令也可以被形象的转换为两条伪指令:
pushl %eip(*) movl $0x1234, %eip(*)
- 即:将下一条将要运行的指令的地址存入堆栈,并将被调用函数的首指令的地址放入指针寄存器(在真正编写汇编语言时,不能直接对%eip进行赋值)
ret 指令
-
函数返回指令
在被调用的函数中使用,返回至调用前的位置,其伪指令如下:
popl %eip(*)
即:将之前指令指针寄存器入栈的值重新取出,并赋值给指令指针寄存器,让指令从之前断掉的地方继续开始运行。
二、虚拟一个X86的CPU实验平台
-
在实验楼中使用代码许你一个虚拟的x86实验平台,在这里我们可以找到一个简单的时间片轮转多道程序的内核代码,具体操作如下,输入代码:
$ cd ./LinuxKernel/linux-3.9.4 $ rm -rf mykernel $ patch -p1 <../mykernel_for_linux3.9.4sc.patch $ make allnoconfig
-
在主函数中,经过十万次循环就输出打印一次 i 的值
my_start_kernel here i \n
-
在中断函数中,中断的函数入口以及过程:
中断检测-->进入中断-->中断处理-->中断返回*
都已经为我们设置好了,我们只需要编写进入中断后函数处理的动作即可。
简单的主函数示意图如下图所示:
简单的中断函数示意图如下图所示:
做一个简单的内核系统
下面是一个简单的时间片轮转多道程序内核代码,用以产生不同的系统调度效果。
/* 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 */
char 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结构体
task_entry:进程入口函数
thread:保存eip和esp
state:进程状态,用于判断是否调度
#define MAX_TASK_NUM 10 // max num of task in system
#define KERNEL_STACK_SIZE 1024*8
#define PRIORITY_MAX 30 //priority range from 0 to 30
/* CPU-specific state of this task */
struct Thread {
unsigned long ip;//point to cpu run address
unsigned long sp;//point to the thread stack's top address
//todo add other attrubte of system thread
};
//PCB Struct
typedef struct PCB{
int pid; // pcb id
volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */
char stack[KERNEL_STACK_SIZE];// each pcb stack size is 1024*8
/* CPU-specific state of this task */
struct Thread thread;
unsigned long task_entry;//the task execute entry memory address
struct PCB *next;//pcb is a circular linked list
unsigned long priority;// task priority ////////
//todo add other attrubte of process control block
}tPCB;
//void my_schedule(int pid);
void my_schedule(void);
其中的内嵌汇编代码为核心:
"pushl %%ebp\n\t" /* save ebp */
"movl %%esp,%0\n\t" /* save esp */
"movl %2,%%esp\n\t" /* restore esp */
"movl %2,%%ebp\n\t" /* restore ebp */
"movl $1f,%1\n\t" /* save eip */
"pushl %3\n\t"
"ret\n\t" /* restore eip */
: "=m" (prev->thread.sp),"=m" (prev->thread.ip)
: "m" (next->thread.sp),"m" (next->thread.ip)
"pushl %%ebp\n\t" /* save ebp */
-
中断处理函数:
void my_timer_handler(void) { #if 1 // 2000次系统时钟循环周期 if(time_count%2000 == 0 && my_need_sched != 1) { my_need_sched = 1; //time_count=0; } time_count ++ ; #endif return; } void all_task_print(void); tPCB * get_next(void) { int pid,i; tPCB * point=NULL; tPCB * hig_pri=NULL;//points to the the hightest task all_task_print(); hig_pri=my_current_task; for(i=0;i<MAX_TASK_NUM;i++) if(task[i].priority<hig_pri->priority) hig_pri=&task[i]; printk(" higst process is:%d priority is:%d\n",hig_pri->pid,hig_pri->priority); return hig_pri; }//end of priority_schedule void my_schedule(void) { tPCB * next; tPCB * prev; // 如果进程或者只有一个进程,将不再需要pcb if(my_current_task == NULL || my_current_task->next == NULL) { printk(KERN_NOTICE " time out!!!,but no more than 2 task,need not schedule\n"); return; } next = get_next(); prev = my_current_task; printk(KERN_NOTICE " the next task is %d priority is %u\n",next->pid,next->priority); if(next->state == 0)/* -1 unrunnable, 0 runnable, >0 stopped */ {//save current scene /* switch to next process */ asm volatile( "pushl %%ebp\n\t" /* save 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;//switch to the next task printk(KERN_NOTICE " switch from %d process to %d process\n >>>process %d running!!!<<<\n\n",prev->pid,next->pid,next->pid); } else { next->state = 0; my_current_task = next; printk(KERN_NOTICE " switch from %d process to %d process\n >>>process %d running!!!<<<\n\n\n",prev->pid,next->pid,next->pid); asm volatile( "pushl %%ebp\n\t" /* save ebp */ "movl %%esp,%0\n\t" /* save esp */ "movl %2,%%esp\n\t" /* restore esp */ "movl %2,%%ebp\n\t" /* restore ebp */ "movl $1f,%1\n\t" /* save eip */ "pushl %3\n\t" "ret\n\t" /* restore eip */ : "=m" (prev->thread.sp),"=m" (prev->thread.ip) : "m" (next->thread.sp),"m" (next->thread.ip) ); } return; }//end of my_schedule void all_task_print(void) { int i,cnum=62;// printk(KERN_NOTICE "\n current task is:%d all task in OS are:\n",my_current_task->pid); printk(" "); for(i=0;i<cnum;i++) printk("-"); printk("\n | process:"); for(i=0;i< MAX_TASK_NUM;i++) printk("| %2d ",i); printk("|\n | priority:"); for(i=0;i<MAX_TASK_NUM;i++) printk("| %2d ",task[i].priority); printk("|\n "); for(i=0;i<cnum;i++) printk("-"); printk("\n"); }