• 201920201 20199320《Linux内核原理与分析》第三周作业


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

    一、虚拟一个x86的CPU硬件平台

    1. 在实验楼环境下输入如下命令:

      > cd ~/LinuxKernel/linux-3.9.4
      > qemu -kernel arch/x86/boot/bzImage
      

      得到如图结果:

    2. QEMU窗口不显示输出结果,为方便理解内核启动效果,重新输入如下命令:

      > cd ~/LinuxKernel/linux-3.9.4
      > rm -rf mykernel
      > patch -p1 < ../mykernel_for_linux3.9.4sc.patch
      > make allnoconfig
      > qemu -kernel arch/x86/boot/bzImage
      

      经过make后,可在QEMU窗口清楚看到内核启动效果:

    3. 进入mykernel目录,可以看到QEMU输出内容的代码mymain.c和myinterrupt.c,如图:

      mymain.c的执行功能是每100000次输出一次“my_start_kernel here i”,同时有一个中断处理程序的上下文环境,周期性地产生时钟中断信号,能够触发myinterrupt.c中的代码(如下图),输出“>>>my_timer_hender here<<<<”。

    二、在mykernel基础上构造一个简单的操作系统内核

    1. 增加一个 mypcb.h 头文件,用来定义进程控制块,即进程结构体的定义,具体代码请点击“mypcb.h”;

    2. mymain.c 进行修改,这里是mykernel内核代码的入口,负责初始化内核的各个组成部分。现对部分关键代码进行分析:

      • 启动第一个进程
       /* start process 0 by task[0] */
          pid = 0;
          my_current_task = &task[pid];
      	asm volatile(
          	"movl %1,%%esp\n\t" 	/* 将进程原堆栈栈顶的地址(这里是初始化的值)存入ESP寄存器 */
          	"pushl %1\n\t" 	        /* 将当前EBP寄存器值入栈 */
          	"pushl %0\n\t" 	        /* 将当前进程的EIP(这里是初始化的值)入栈 */
          	"ret\n\t" 	            /* ret命令正好可以让入栈的进程EIP保存到EIP寄存器中 */
          	: 
          	: "c" (task[pid].thread.ip),"d" (task[pid].thread.sp)	/* input c or d mean %ecx/%edx*/
      	);
      } 
      

      具体分析如下图:

      附:这里用到vi编辑器的全删除,可输入可输入:1,$d从第一行删到末尾,其他删除操作点击这里访问。

    3. interrupt.c 进行修改,主要是增加了进程切换的代码,现对关键代码进行分析:

      • 进程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)
          );
      }
      

      三、总结

      本章主要借助Linux内核部分源代码模拟了计算机3法宝(存储程序计算机、函数调用堆栈、中断),通过认真对源代码中汇编部分的分析,基本了解了进程的切换,进程在执行过程中,如果有需要调度其他进程时,需先保存当前进程的上下文环境,当该进程被调度时需先恢复上下文环境,以此实现了进程的并发执行,大大提高了计算机运行效率。

  • 相关阅读:
    关于python2.7的md5加密遇到的问题(TypeError: Unicode-objects must be encoded before hashing)
    Spring @Resource、@Autowired、@Qualifier区别
    互联网秒杀设计--转载
    学习动态性能表(5)--v$session
    学习动态性能表(4)--v$sqltext&v$sqlarea
    学习动态性能表(3)--v$sql&v$sql_plan
    学习动态性能表(2)--v$sesstat
    学习动态性能表(1)--v$sysstat
    Oracle备份与恢复案例
    日梭万年历网络版
  • 原文地址:https://www.cnblogs.com/liangxu111/p/11604983.html
Copyright © 2020-2023  润新知