zoukankan      html  css  js  c++  java
  • Linux内核设计第二周学习总结 完成一个简单的时间片轮转多道程序内核代码

    陈巧然 原创作品 转载请注明出处 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

    一、使用实验楼的虚拟机, 观察只有一个死循环的mykernel与时钟中断的关系

    步骤:
    cd LinuxKernel/linux-3.9.4 qemu -kernel arch/x86/boot/bzImage

    执行效果如下图

    Paste_Image.png
    Paste_Image.png

    现在查看mymain.c:

    Paste_Image.png
    Paste_Image.png

    再查看myinterrupt.c:

    Paste_Image.png
    Paste_Image.png

    从执行效果看,my_timer_handler 与 my_start_kernel 中死循环确实是交替执行的,每循环约100,000次会执行一次timer_handler。

     

    二、为只有死循环的mykernel加入时间片功能并重新编译,观察新的mykernel的行为

    首先clone mengning/mykernel,替换mymain.c 和 myinterrupt.c, 增加mypcb.h:

    cd ~/LinuxKernel/linux-3.9.4
    git clone https://github.com/mengning/mykernel.git mykernel_new
    cd mykernel_new
    cp mymain.c myinterrupt.c mypcb.h ../mykernel
    cd ..

    然后运行make 重新编译mykernel, 如图:

    Paste_Image.png
    Paste_Image.png

    然后再运行qemu -kernel arch/x86/boot/bzImage:

    不难观察到新的mykernel的行为, 总共有0 1 2 3 共四个process, 新的mykernel 执行n号process一定时间后,会换到(n+1)%4号process继续执行,
    在替换时时会打印>>> my_schedule <<<, 和>>> switch n to (n+1)%4 <<<
    如下图:

    3号进程切到0号的瞬间:

    Paste_Image.png
    Paste_Image.png

    1号进程切到2号的瞬间:

    Paste_Image.png
    Paste_Image.png

    知道了mykernel的行为,下面来分析mymain.c 和 myinterrupt.c 是如何做到这些的:
    首先可以在mypcb.h的第10行看到一个常量定义

    #define MAX_TASK_NUM        4

    再观察mykernel执行入口函数 my_start_kernel 在 mymain.c 从第36行开始的循环

        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(
            "movl %1,%%esp
    	"     /* set task[pid].thread.sp to esp */
            "pushl %1
    	"             /* push ebp */
            "pushl %0
    	"             /* push task[pid].thread.ip */
            "ret
    	"                 /* pop task[pid].thread.ip to eip */
            "popl %%ebp
    	"
            : 
            : "c" (task[pid].thread.ip),"d" (task[pid].thread.sp)    /* input c or d mean %ecx/%edx*/
        );
    }

    结合代码注释,可以得出:36行以上的代码初始化了0号process的pcb,并将进程设为runnable,而且将执行入口设为my_process 在36行开始的循环中,依次初始化了1 2 3号,设为unrunnable,并将0 1 2 3 号process的next指针 分别设为 1 2 3 0的地址,(形成一个单循环链表), 并设置各自thread.sp指针为各自内核栈的起始地址。然后在L48到L55的汇编代码中,先将当前esp设为task[0].thread.sp,并入栈保存,
    然后通过push/ret的方式,间接call了0号process的thread.ip地址处的my_process函数。之后的pop %ebp是下一个被调度到的process第一个执行的代码

    到了my_process函数中, 每循环10000000此后,先判断my_need_sched, 若之前my_timer_handler中将my_need_schedule置1了(每1000次时钟中断一次),则进入my_schedule并将my_need_sched置0;

    现在到了负责调度了my_schedule函数:
    若next process第一次执行(state == -1),则按else 分支中的流程:
    入栈ebp, 保存当前esp 到prev->thread.sp, eip到 prev->thread.ip, 然后将ebp, esp设置为next->thread.sp, 然后与0号process同样的方法(push thread.ip; ret)来call my_process函数。

    若next process 又一次被调度(state == 0), 则按56至69行执行。

    总结:调度的实现需要保存当前task/process的现场(ebp/eip),然后配合时钟中断,对第一次被调度和再次被调度分情况处理。

  • 相关阅读:
    windows下Yarn安装与使用(两种方法)
    git配置公钥---解决码云出现git@gitee.com: Permission denied (publickey)
    npm使用国内镜像的两种方法
    【LeetCode】33. Search in Rotated Sorted Array (4 solutions)
    【LeetCode】83. Remove Duplicates from Sorted List
    【LeetCode】82. Remove Duplicates from Sorted List II
    【LeetCode】85. Maximal Rectangle
    【LeetCode】84. Largest Rectangle in Histogram
    【LeetCode】87. Scramble String
    【LeetCode】162. Find Peak Element (3 solutions)
  • 原文地址:https://www.cnblogs.com/20135310cqr/p/5232564.html
Copyright © 2011-2022 走看看