吕松鸿+ 原创做品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000linux
存储程序计算机工做模型,计算机系统最最基础性的逻辑结构;shell
函数调用堆栈,高级语言得以运行的基础,只有机器语言和汇编语言的时候堆栈机制对于计算机来讲并不那么重要,但有了高级语言及函数,堆栈成为了计算机的基础功能;markdown
enter 框架
pushl %ebp函数
movl %esp,%ebpui
leave this
movl %ebp,%espspa
popl %ebp操作系统
函数参数传递机制和局部变量存储线程
中断,多道程序操做系统的基点,没有中断机制程序只能从头一直运行结束才有可能开始运行其余程序。
了解对站存在的目的和编译器对堆栈使用的规则是理解操做系统一些关键性代码的基础。
堆栈相关寄存器:
esp:堆栈指针(stack pointer),指向系统栈最上面一个栈帧的栈顶
ebp: 基址指针(base pointer),指向系统栈最上面一个栈帧的底部
cs:eip:指令寄存器(extended instruction pointer),指向下一条等待执行的指令地址
pushl %ebp
和movl %esp,%ebp
,leave是这两条指令的反操做。call指令的两个做用:
- 将下一条指令的地址A保存在栈顶 - 设置eip指向被调用程序代码开始处


执行call function
cs:eip原来的值指向call下一条指令,该值被保存到栈顶 cs:eip的值指向function的入口地址
进入function
pushl %ebp //意为保存调用者的栈帧地址 movl %esp, %ebp //初始化function的栈帧地址 而后函数体中的常规操做
退出function
movl %ebp,%esp popl %ebp ret
(一)mykernel实验
实验步骤
(1)进入实验楼,打开shell以后按照说明输入
(2)查看源代码
a.查看mymain.c
以前的#include...都是硬件初始化用到的语句,而截图部分是是“操做系统”开始执行的入口
从代码可见,每循环10 000次,打印一句话。
b.查看myinterrupt.c
每执行一次,都会执行一次时钟中断
(二)在mykernel基础上构造一个简单地操做系统内核
/* * linux/mykernel/mypcb.h * Kernel internal PCB types * Copyright (C) 2013 Mengning */ #define MAX_TASK_NUM 4 #define KERNEL_STACK_SIZE 1024*8 /* CPU-specific state of this task */ struct Thread { unsigned long ip;//保存eip unsigned long sp;//保存esp }; 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);//调度器
/* * linux/mykernel/mymain.c * Kernel internal my_start_kernel * Copyright (C) 2013 Mengning */ #include <linux/types.h> #include <linux/string.h> #include <linux/ctype.h> #include <linux/tty.h> #include <linux/vmalloc.h> #include "mypcb.h" tPCB task[MAX_TASK_NUM]; tPCB * my_current_task = NULL; volatile int my_need_sched = 0;//用来判断是否须要调度的标识 void my_process(void); void __init my_start_kernel(void) { int pid = 0; int i; /* Initialize process 0 (初始化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号进程的入口:myprocess task[pid].thread.sp = (unsigned long)&task[pid].stack[KERNEL_STACK_SIZE-1]; task[pid].next = &task[pid];//因为0号进程初始化时只有这一个进程,因此next指向本身 /*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( //%0表示参数thread.ip,%1表示参数thread.sp。 "movl %1,%%esp\n\t" /* set task[pid].thread.sp to esp 把参数thread.sp放到esp中*/ "pushl %1\n\t" /* push ebp 因为当前栈是空的,esp与ebp指向相同,因此等价于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*/ ); } void my_process(void) { int i = 0; while(1) { i++; if(i%10000000 == 0) { printk(KERN_NOTICE "this is process %d -\n",my_current_task->pid); if(my_need_sched == 1) { my_need_sched = 0; my_schedule(); } printk(KERN_NOTICE "this is process %d +\n",my_current_task->pid); } } }
my_need_sched
判断是否须要调度,一旦发现其值为1,就调用my_schedule完成进程的调度。/* * linux/mykernel/myinterrupt.c * Kernel internal my_timer_handler * Copyright (C) 2013 Mengning */ #include <linux/types.h> #include <linux/string.h> #include <linux/ctype.h> #include <linux/tty.h> #include <linux/vmalloc.h> #include "mypcb.h" extern tPCB task[MAX_TASK_NUM]; extern tPCB * my_current_task; extern volatile int my_need_sched; volatile int time_count = 0; /* * 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) { printk(KERN_NOTICE ">>>my_timer_handler here<<<\n"); my_need_sched = 1; } time_count ++ ; #endif return; } void my_schedule(void) { tPCB * next; tPCB * prev; if(my_current_task == NULL //task为空,即发生错误时返回 || my_current_task->next == NULL) { 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 */ /*若是下一个进程的状态是正在执行的话,就运用if语句中的代码表示的方法来切换进程*/ asm volatile( "pushl %%ebp\n\t" /* save ebp 保存当前进程的ebp*/ "movl %%esp,%0\n\t" /* save esp 把当前进程的esp赋给%0(指的是thread.sp),即保存当前进程的esp*/ "movl %2,%%esp\n\t" /* restore esp 把%2(指下一个进程的sp)放入esp中*/ "movl $1f,%1\n\t" /* save eip $1f是接下来的标号“1:”的位置,把eip保存下来*/ "pushl %3\n\t" /*把下一个进程eip压栈*/ "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//用于下一个进程为未执行过的新进程时。首先将这个进程置为运行时状态,将这个进程做为当前正在执行的进程。 { next->state = 0; my_current_task = next; printk(KERN_NOTICE ">>>switch %d to %d<<<\n",prev->pid,next->pid); /* switch to new process */ 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; }
1.操做系统“两剑”:中断上下文、进程上下文的切换.
2.操做系统的核心功能就是:进程调度和中断机制,经过与硬件的配合实现多任务处理,再加上上层应用软件的支持,最终变成能够使用户能够很容易操做的计算机系统。
3.进程切换机制中包含esp的切换、堆栈的切换。从esp能够找到进程的描述符;堆栈中ebp的切换,肯定了当前变量空间属于哪一个进程。