2019-2020-1 20199318《Linux内核原理与分析》第三周作业

第2章 操作系统是如何工作的

一、学习笔记

1. 计算机的三大法宝:存储程序计算、函数调用机制、中断。
2. 堆栈的具体作用有:记录函数调用框架、传递函数参数、保存返回值的地址、提供函数内部局部变量的存储空间等。
3. 堆栈相关的寄存器:
   ESP:堆栈指针(stack pointer)
   EBP:基址指针(base pointer),在C语言中用作记录当前函数调用基址。
4. 对于X86体系结构来讲,堆栈空间是从高地址向低地址增长的,如图所示:

5. 堆栈操作:
   push:栈顶地址减少4个字节(32位),并将操作数放入栈顶存储单元。
   pop:栈顶地址增加4个字节(32位),并将栈顶存储单元的内容放入操作数。
6. 其它一些指令:
   顺序执行:总是指向地址连续的下一条指令。
   跳转/分支:执行这样的指令时,CS:EIP的值会根据程序需要被修改。
   call:将当前CS:EIP 的值压入栈顶,CS:EIP指向被调用函数的入口地址。
   ret:从栈顶弹出原来保存在这样的CS:EIP的值,放入CS:EIP中。

二、试验记录

1.进程初始化代码

asm volatile(
    "movl %1,%%esp\n\t"   //将进程原堆栈栈顶的地址(这里是初始化的值)存入ESP寄存器。
    "pushl %1\n\t"        //将当前EBP寄存器值入栈。
    "pushl %0\n\t"        //将当前进程的EIP(这里是初始化的值)入栈。
    "ret\n\t"             //ret命令正好可以让入栈的进程EIP保存到EIP寄存器中。
    "popl %%ebp\n\t"      //与前面push指令结对出现。

2.进程0启动,开始执行my_process(void)函数的代码。

if(next->state==0)//next->state==0对应进程next对应进程曾经执行过。
{
    //进行进程调度关键代码。
    asm volatile(
        "pushl %%ebp\n\t"   //保存当前EBP到堆栈中。
        "movl %%esp,%0\n\t" //保存当前ESP到当前PCB中。
        "movl %2,%%esp\n\t" //将next进程的堆栈栈顶的值存到ESP寄存器。
        "movl $1f,%1\n\t"   //保存当前进程的EIP值,下次恢复进程后将在标号1开始执行。
        "pushl %3\n\t"      //将next进程继续执行的代码位置(标号1)压栈。
        "ret\n\t"           //出栈标号1到EIP寄存器。
        "1:\t"              //标号1,即next进程开始执行的位置。
        "pop1 %%ebp\n\t"    //恢复EBP寄存器的值。
        : "=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);
//转换到新的进程中
    asm volatile(
      "pushl %%ebp\n\t"   //保存当前EBP到堆栈中。
      "movl %%esp,%0\n\t" //保存当前ESP到当前PCB中。
      "movl %2,%%esp\n\t" //载入next进程的栈顶地址到ESP寄存器。
      "movl %2,%%ebp\n\t" //载入next进程的堆栈基地址到EBP寄存器
      "movl $1f,%1\n\t"   //保存当前EIP寄存器值到PCB,这里$1f是指上面的标号1。
      "push %3\n\t"       //把即将执行的进程的代码入口地址入栈。
      "ret\n\t"           //出栈进程的代码入口地址到EIP寄存器。
      : "=m" (prev->thread.sp),"=m" (prev->thread.ip)
      : "m" (next->thread.sp),"m" (next->thread.ip)
    );
}

3.对上述代码的堆栈调用过程进行分析。

为了分析简单,假设系统只有两个进程,分别是进程0和进程1.进程0由内核启动时初始化执行,然后需要进程调度,开始执行进程1。下面从进程1被调度开始分析堆栈变化,因为进程1从来没有被执行过,是第一次被调度执行,此时执行else中的代码。

到这里开始执行进程1,如果进程1执行的过程中发生了进程调度,进程0重新被调度执行了,应该执行前述if中的代码,具体的堆栈变化与上图所示过程相似。

总结

本章内容最重要的是进程的切换,进程在执行过程中,当时间片用完需要进程切换时,需要先保存当前的进程执行的上下文环境,下次进程被调度时,需要恢复进程的上下文环境。这样实现多道程序的并发执行。

posted @ 2019-09-28 17:41  孙茂林  阅读(215)  评论(0编辑  收藏  举报