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),然后配合时钟中断,对第一次被调度和再次被调度分情况处理。

  • 相关阅读:
    用js添加网页标题时,在QQ里无效,标题栏空白
    用css3的@keyframes里设置transform:rotate(); 当控制动画暂停:animation-play-state:paused暂停,在微信和safari里无效
    Python可变序列中的一些坑,记得多注意
    你知道?Python 中的序列类型支持哪些公共操作吗?
    用 python print() 函数实现的三个特效
    教你使用python生成器重构提取数据方法,来优化你的爬虫代码
    python中主线程与子线程的结束顺序,你知道吗?
    python装饰器实现对异常代码出现进行自动监控
    Python教程:高效率遍历文件夹寻找重复文件
    python教程: filter()和reduce()函数用法详解
  • 原文地址:https://www.cnblogs.com/20135310cqr/p/5232564.html
Copyright © 2011-2022 走看看