zoukankan      html  css  js  c++  java
  • 《30天自制操作系统》15_day_学习笔记

    harib12a:
      这一部分我们来尝试两个任务的切换。下面我们一步一步的看:

      1、定义TSS任务状态段(task statuc segment);定义的一种段,需要在GDT中定义使用

    //TSS任务状态段(task statuc segment)
    struct TSS32 {//26个int成员,104字节
        //与任务设置相关的信息(任务切换时,除backlink,都不会被写入)
        int backlink, esp0, ss0, esp1, ss1, esp2, ss2, cr3;
        //32位寄存器;eip任务返回时,找到返回的地址
        int eip, eflags, eax, ecx, edx, ebx, esp, ebp, esi, edi;
        //16位寄存器
        int es, cs, ss, ds, fs, gs;
        //第一行一样,有关任务设置的信息。任务切换时CPU不写
        //ldtr = 0; iomap = 0x4000_0000
        int ldtr, iomap;
    };

      2、尝试两个任务的切换。A和B

    //(1)、两个任务定义
      struct TSS32 tss_a, tss_b;
    //(2)、任务初始化:ldtr和iomap赋初值
        tss_a.ldtr = 0;
        tss_a.iomap = 0x40000000;
        tss_b.ldtr = 0;
        tss_b.iomap = 0x40000000;
    //(3)、在GDT中定义3号、4号
        set_segmdesc(gdt + 3, 103, (int) &tss_a, AR_TSS32);
        set_segmdesc(gdt + 4, 103, (int) &tss_b, AR_TSS32);

      3、TR(task  register)寄存器让CPU记住当前运行哪一个任务(GDT中任务号*8)

        //HariMain
        load_tr(3 * 8);
        //naskfunc.nas
        _load_tr:        ; void load_tr(int tr);
            LTR        [ESP+4]            ; tr
            RET
        //任务切换函数
        _taskswitch4:    ; void taskswitch4(void);
            JMP        4*8:0;4*8用来指向TSS。这个是任务段啊!哥哥
            RET        ;从汇编语言返回到C语言执行

      4、程序执行10s后进行任务切换

    void HariMain(){
        //.......B的任务栈
        tss_b.esp = task_b_esp;//B的任务栈
        task_b_esp = memman_alloc_4k(memman, 64 * 1024) + 64 * 1024;
        //.......
        else if (i == 10) { /* 10秒定时器超时 */
            putfonts8_asc_sht(sht_back, 0, 64, COL8_FFFFFF, COL8_008484, "10[sec]", 7);
            taskswitch4();  //进行任务切换
        } else if (i == 3) { /* 3昩僞僀儅 */
        //......
    }

    harib12b:
      上一步,我们让任务B切换到任务A;下面再切回任务A 。改写task_b_main();最后调用taskswitch3()切回任务A:

    void task_b_main(void)
    {    //.....
        timer = timer_alloc();
        timer_init(timer, &fifo, 1);
        timer_settime(timer, 500);
        //....定时器超时时间为5s,5s后切回任务A。3号
        taskswitch3(); 
    }   //  跳到GDT中3号段的位置
    _taskswitch3:    ; void taskswitch3(void);
        JMP        3*8:0
        RET

    harib12c:

      上面我们进行了任务A、B之间的切换,方法是为AB都写一个任务入口的函数,在这个函数中调用teskswitch();然而在真正的系统中,如果我们有100个任务,难道我们要去为这100个任务写100个处理函数?显然这不是我们想要的,接下来我们来实现一个通用函数来进行任务切换。

    ;函数:farjmp(eip,cs);
    ;例如:farjmp(0,4*8);表示切换到GDT的4号段的任务
    _farjmp:        ; void farjmp(int eip, int cs);
        JMP        FAR    [ESP+4]       ; eip, cs
        RET

      接着准备一个任务切换的计时器。timer_ts,用每0.02s执行一次任务的切换。每次farjmp切换返回的时候,将定时器重新设定到0.02s后,让程序返回0.02s后再次执行任务切换。

    //HariMain部分代码:
    load_tr(3 * 8)//初始化TR寄存器位3号,任务A运行
    for (;;) {
        //...
        farjmp(0, 4 * 8);//跳到4号,任务B
        timer_settime(timer_ts, 2);//延时0.02s
        //...
    }
    void task_b_main(void)//任务B做的事情。
    {    //...
        farjmp(0, 3 * 8);跳到3号,任务A
        timer_settime(timer_ts, 2);//这个好像没有执行啊!!!???
    }

    harib12d:
      sht_back背景图层实在HariMain中定义的。怎么让void task_b_main(void)知道sht_back的值?我们将将sht_back的地址写到内存0x0fec中。然后再task_b_main(void)中读出来:

    //HariMain将sht_back写到内存中。
    *((int *) 0x0fec) = (int) sht_back;
    //task_b_main将sht_back的值读出来
    sht_back = (struct SHEET *) *((int *) 0x0fec);

    harib12e:

      我们发现虽然上面实现任务之间的不断切换,但是在真机上的运行时间太慢了。因为count每计数一次就刷新一次。然而我们并不要求刷新这么频繁,人眼是分辨不出来的。我们只需要0.01s刷新一次就行了/。修改task_b_main()
      sht_back从HM中传过来的方法:将sht_back的地址放到任务B段的起始位置,这样任务B在执行task_b_main时,会把开始的4个字节的数据当作参数*sht_back的地址。另外,task_b_main实际上就是任务B执行的内容。没有任何函数调用,不用return来返回调用处。

    void task_b_main(struct SHEET *sht_back)
    {
        struct FIFO32 fifo;          //32位的FIFO缓冲区
        struct TIMER *timer_ts, *timer_put;//两个定时器
        int i, fifobuf[128], count = 0;
        char s[12];
    
        fifo32_init(&fifo, 128, fifobuf);//FIFO缓冲区初始化fifobuf[128]
        timer_ts = timer_alloc();     //任务切换定时器,0.02s
        timer_init(timer_ts, &fifo, 2);  //数据为2
        timer_settime(timer_ts, 2);
        
        timer_put = timer_alloc();    //刷新(输出)定时器,0.01s
        timer_init(timer_put, &fifo, 1); //数据为1
        timer_settime(timer_put, 1);
    
        for (;;) {
            count++;
            io_cli();
            if (fifo32_status(&fifo) == 0) {   //缓冲区为空
                io_sti();
            } else {
                i = fifo32_get(&fifo);      //超时了,获得定时器号
                io_sti();
                if (i == 1) {           //数据为1,刷新定时器超时
                    sprintf(s, "%11d", count);
                    putfonts8_asc_sht(sht_back, 0, 144, COL8_FFFFFF, COL8_008484, s, 11);
                    timer_settime(timer_put, 1);//输出后,在设定定时器1
                } else if (i == 2) {       //数据为2,任务切换定时器。
                    farjmp(0, 3 * 8);      //切换任务后再设定定时器2
                    timer_settime(timer_ts, 2);
                }
            }
        }
    }

    harib12f:
      上面我们将刷新频率固定到0.01s一次。这里我们来测试一些程序的运行速度。在task_b_main中加入一些内容:

    void task_b_main(struct SHEET *sht_back)
    {    //......增加一个定时器
        timer_1s = timer_alloc();
        timer_init(timer_1s, &fifo, 100);
        timer_settime(timer_1s, 100);
        //.....
        if (i == 100) {    //100号;10s
        //这里没100次中断,显示count计数的值。
        sprintf(s, "%11d", count - count0);
        putfonts8_asc_sht(sht_back, 0, 128, COL8_FFFFFF, COL8_008484, s, 11);
        count0 = count;
        timer_settime(timer_1s, 100);
                }
        //........
    }   //接下来笔者把显示计数的定时器去掉,重新测试了一下
        //发现速度的确有所提升,这里主要是要知道测试的方法。*****

    harib12g:
      我们上面的多任务实在HM和TB中写入任务切换来实现的。下面来创建真正的多任务

      1、创建多任务函数

    //mtask.c文件
    #include "bootpack.h"
    struct TIMER *mt_timer;
    int mt_tr;
    //初始化*mt_timer;mt_tr;
    void mt_init(void)
    {
        mt_timer = timer_alloc();//为定时器mt_timer分配内存
        //我们不再需要向FIFO中写数据了
        timer_settime(mt_timer, 2);//设置超时时间0.02s
        mt_tr = 3 * 8;//将要写到GDT的3号段
        return;
    }
    void mt_taskswitch(void)
    {
        if (mt_tr == 3 * 8) {
            mt_tr = 4 * 8;
        } else {
            mt_tr = 3 * 8;
        }
        timer_settime(mt_timer, 2);//再定时0.02s
        farjmp(0, mt_tr);//任务切换
        return;
    }

      2、修改定时器中断inthandler20()

    void inthandler20(int *esp)
    {    //.......
        for (;;) {
            /* timers的计时器全部在工作中,不需要flag */
            if (timer->timeout > timerctl.count) {
                break;           //没有超时,不发生中断。回去
            }
            /* 超时了,下面进行中断处理 */
            timer->flags = TIMER_FLAGS_ALLOC;
            if (timer != mt_timer) {  //发生中断的定时器不是*mt_timer;
                              //向FOFO写数据
                fifo32_put(timer->fifo, timer->data);
            } else {
            //*mt_timer;不用想FIFO写数据
                ts = 1;          /* mt_timer超时了 */
            }
            timer = timer->next;     /* 下一个计时器 */
        }
        timerctl.t0 = timer;       //t0放定时器链表的第一个地址
        timerctl.next = timer->timeout;//下一个定时器的超时时间。
        if (ts != 0) {
            //ts就是用来进行任务AB切换的标志
            mt_taskswitch();
        }
        return;
    }

      3、接下来,删除bootpack.c中原来的任务A.B切换的代码即可!

      QUE:为什么不在tm_timer超时的地方直接调用mt_taskswitch();?
      ANS:调用mt_taskswitch();进行任务切换的时候,即便中断还没有处理完成,IF(中断允许标识)可能会被重设回1,(因为任务切换的时候同时会被切换EFLAGS),这样,在定时器中断还没有处理完成的时候,会产生下一个中断请求,导致程序出错。记住:任务可以切换,中断时不能切换的。中断必须处理完成后,才能处理下一个中断,不然会有意想不到的错误。

  • 相关阅读:
    Android JS 交互出现 Uncaught Error: Error calling method on NPObject
    adapter.notifydatasetchanged()没有效果
    Android 正则表达式验证手机号码
    Android SpannableString实现TextView的点击事件
    使用Jquery的Ajax调用
    我们常用,却容易忽视——CSS的BFC(Block formatting contexts)
    React数据流和组件间的通信总结
    CSS清除浮动float方法总结
    CSS3幻灯片制作心得
    JavaScript中map函数和filter的简单举例
  • 原文地址:https://www.cnblogs.com/pengfeiz/p/5812929.html
Copyright © 2011-2022 走看看