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

    harib10a:

      简化字符串的显示:我们发现字符串显示三条语句总是重复出现,并且总是一起出现的。接下来我们把它归纳到一个函数中,这样便于使用。

    • x,y--位置的坐标
    •    c--字符颜色  (color)
    •    b--背景颜色  (back color)
    •    s--字符串     (string)
    •    l--字符串长度(length)
    void putfonts8_asc_sht(struct SHEET *sht, int x, int y, int c, int b, char *s, int l)
    {
        boxfill8(sht->buf, sht->bxsize, b, x, y, x + l * 8 - 1, y + 15);//涂背景颜色
        putfonts8_asc(sht->buf, sht->bxsize, x, y, c, s);            //在背景上写字符串
        sheet_refresh(sht, x, y, x + l * 8, y + 16);            //完成刷新
        return;
    }

    harib10b:
      重新调整FIFO缓冲区(01):前面我们设置了3个定时器,就需要3个FIFO缓冲区;如果我们需要100个定时器,难道需要malloc()100个缓冲区吗?下面我们通过把定时器用的多个FIFO缓冲区集中在1个上面来对此进行优化,我们通过往FIFO中写入不同的数据来分辨出是哪个定时器超时了。

        //HariMain节选
        fifo8_init(&timerfifo, 8, timerbuf);//只初始化一个FIFO缓冲区
        timer = timer_alloc();  //第一个定时器,向缓冲区中写入数据10
        timer_init(timer, &timerfifo, 10);
        timer_settime(timer, 1000);
        timer2 = timer_alloc(); //第二个定时器,向缓冲区中写入数据3
        timer_init(timer2, &timerfifo, 3);
        timer_settime(timer2, 300);
        timer3 = timer_alloc(); //第三个定时器,向缓冲区中写入数据1
        timer_init(timer3, &timerfifo, 1);
        timer_settime(timer3, 50);
        //接下来:...................
        //1、判断fifo8_status(&timerfifo)!=0;定时器缓冲区开辟成功
        //2、判断i = fifo8_get(&timeerfifo);根据i的值判断是哪个定时器超时

    harib10c--harib10f:
      前面我们在对定时器不断进行改进,下面来看看改进的效果,测试一下性能。

      测试方法:先修改HariMain恢复变量count,然后完全不现实计数,执行count++;在启动3秒后,把count复位为0一次。当到了10s后超时的时候,再显示这个count的值。(为什么要在系统启动3s后,把count复位为0。因为系统在刚启动进行初始化时,所花费的时间会因为某些条件发生很大的变化,3s后复位可以消除因为系统启动的不确定性给测试带来的影响。)

      结果说明:下面的结果可以看出,每改良一次速度就会提高;这些测试数据都是笔者在真机上运行的结果。在模拟器上,会受到Windows的影响,导致结果的波动性较大;

      • harib10d:  0074619985(利用harib09d时的timer.c和bootpack.c;最初的定时器)
      • harib10e:  0074629087(利用harib09e时的timer.c和bootpack.c;timeout改进为记忆超时时刻)
      • harib10f:   0074633350(利用harib09f时的timer.c和bootpack.c;导入了next表示下一个定时器时刻)
      • harib10c:  0074643595(导入timers [ ] )
        //修改后的HariMain
        if (fifo8_status(&timerfifo) != 0) {      //定时器缓冲区开辟成功
        i = fifo8_get(&timerfifo);            /* 获得FIFO中定时器的DATA,看哪个定时器超时 */
        io_sti();
        if (i == 10) {                  //获取的数据为10,第一个定时器,10s
          putfonts8_asc_sht(sht_back, 0, 64, COL8_FFFFFF, COL8_008484, "10[sec]", 7);
          sprintf(s, "%010d", count);
          putfonts8_asc_sht(sht_win, 40, 28, COL8_000000, COL8_C6C6C6, s, 10);
        } else if (i == 3) {              //获取的数据为3,第二个定时器,3s
                        putfonts8_asc_sht(sht_back, 0, 80, COL8_FFFFFF, COL8_008484, "3[sec]", 6);
                        count = 0;             /* 應掕奐巒 */
        } else {                     //第三个定时器,闪烁定时器
          if (i != 0) {                //i=1;将定时器数据设定为0;之后显示背景
            timer_init(timer3, &timerfifo, 0);
            boxfill8(buf_back, binfo->scrnx, COL8_FFFFFF, 8, 96, 15, 111);
          } else {                   //i=0;将定时器数据设定为1;之后显示背景
            timer_init(timer3, &timerfifo, 1);
            boxfill8(buf_back, binfo->scrnx, COL8_008484, 8, 96, 15, 111);
          }
          timer_settime(timer3, 50);        //设定第三个定时器时间,50个中断时间
          sheet_refresh(sht_back, 8, 96, 16, 112);//刷新背景图层
        }

    harib10g:
      重新调整FIFO缓冲区(02):在上一次调整FIFO缓冲区时,我们把三个定时器归纳到一个FIFO缓冲区中。这里,我们再将鼠标壳键盘的缓冲区也归纳起来,用1个FIFO缓冲区来管理。我们将FIFO缓冲区的大小从8位扩展到32位(将char型变为int型)来进行改进。先来设置往FIFO中写入数值的中断类型。

      •          0-1:光标闪烁用定时器
      •             3:3秒定时器
      •           10:10秒定时器
      • 256-511:键盘输入(从键盘控制器读入的值+256)
      • 512-767:鼠标输入(从鼠标控制器读入的值+512)

        1、重写FIFO结构体、初始化、写数据、取数据、存储状态函数(FIFO.C有点长,我们折起来吧!)

    struct FIFO32 { //扩展结构体为32位
        int *buf;
        int p, q, size, free, flags;
    };
    /* 以下是:FIFO.C */
    
    #include "bootpack.h"
    #define FLAGS_OVERRUN        0x0001
    
    void fifo32_init(struct FIFO32 *fifo, int size, int *buf)
    /* FIFO缓冲区的初始化 */
    {
        fifo->size = size;          //缓冲区大小
        fifo->buf = buf;           //缓冲区地址
        fifo->free = size;          //缓冲区全空
        fifo->flags = 0;          //标志位:0表示有空余;FLAGS_OVERRUN表示溢出了
        fifo->p = 0;             /* 写入位置指向头部 */
        fifo->q = 0;             /* 读取位置指向头部 */
        return;
    }
    int fifo32_put(struct FIFO32 *fifo, int data)
    /* 向FIFO缓冲区发送数据 */
    {
        if (fifo->free == 0) {
            /* 空闲=0;表示缓冲区已满 */
            fifo->flags |= FLAGS_OVERRUN;//标志位置0x0001溢出
            return -1;           //返回-1,发送数据失败
        }                   //否则下面表示缓冲区未满
        fifo->buf[fifo->p] = data;    //把数据写到下一个位置
        fifo->p++;              //写入位置指针向后移动一个单位
        if (fifo->p == fifo->size) {   //如果写到了buf尾
            fifo->p = 0;           //将写入位置p置0;
        } 
        fifo->free--;             //写入了一个数据,空闲大小减1
        return 0;               //写入成功
    }
    
    int fifo32_get(struct FIFO32 *fifo)
    /* 从FIFO缓冲区的q位置读取一个单位的数据 */
    {
        int data;              //临时变量用来保存读取的数据
        if (fifo->free == fifo->size) {
            /* 此时全空,没有数据可读,返回-1;读取失败 */
            return -1;
        }                   //有数据可读
        data = fifo->buf[fifo->q];    //在q出读取一个数据
        fifo->q++;             //读取位置向后面移动一个
        if (fifo->q == fifo->size) {  //读到了buf尾
            fifo->q = 0;         //读入位置置0
        }
        fifo->free++;           //读取了一个,空闲+1
        return data;            //返回读取的数据
    }
    int fifo32_status(struct FIFO32 *fifo)
       /* 报告已经存储了多少数据 */
    {    //缓冲区的大小减去空闲大小=占用大小
        return fifo->size - fifo->free;
    }
    FIFO结构体+FIFO.C

        2、使用FIFO32修改键盘相关程序

    void init_keyboard(struct FIFO32 *fifo, int data0)//键盘初始化
    {
        /* 保存传入的两个参数到全局变量 */
        keyfifo = fifo;
        keydata0 = data0;
        /* 键盘控制器初始化 */
        wait_KBC_sendready();
        io_out8(PORT_KEYCMD, KEYCMD_WRITE_MODE);
        wait_KBC_sendready();
        io_out8(PORT_KEYDAT, KBC_MODE);
        return;
    }
    void inthandler21(int *esp)          //键盘中断程序
    {
        int data;
        io_out8(PIC0_OCW2, 0x61);          /* 设置PIC芯片信息,IRQ01使能 */
        data = io_in8(PORT_KEYDAT);         //获取键盘读取的数据
        fifo32_put(keyfifo, data + keydata0);  //读取数据+256送到缓冲区中
        return;
    }

        3、用FIFO32修改鼠标相关程序

    void enable_mouse(struct FIFO32 *fifo, int data0, struct MOUSE_DEC *mdec)
    {
        /* 保存传入的参数到全局变量中 */
        mousefifo = fifo;
        mousedata0 = data0;
        /* 等待控制器准备完毕 */
        wait_KBC_sendready();
        io_out8(PORT_KEYCMD, KEYCMD_SENDTO_MOUSE);
        wait_KBC_sendready();
        io_out8(PORT_KEYDAT, MOUSECMD_ENABLE);
        /* 成功返回ACK(0xfa)鼠标初始化完毕 */
        mdec->phase = 0;              /* 等待0xfa返回的阶段 */
        return;
    }
    void inthandler2c(int *esp)         //鼠标的中断程序
    {
        int data;
        io_out8(PIC1_OCW2, 0x64);          /* PIC-01的IRQ12 */
        io_out8(PIC0_OCW2, 0x62);          /* PIC-00的IRQ02 */
        data = io_in8(PORT_KEYDAT);        //获取鼠标数据
        fifo32_put(mousefifo, data + mousedata0);//将数据+512写到FIFO中
        return;
    }

        4、用FIFO32修改定时器结构体

    struct TIMER {
        unsigned int timeout, flags;
        struct FIFO32 *fifo;//加入了FIFO32缓冲区
        int data;
    };
    void inthandler20(int *esp)
    {    //...调用写入函数,将数据写到FIFO缓冲区中。修改相应的传入参数即可
        fifo32_put(timerctl.timers[i]->fifo, timerctl.timers[i]->data);
        /...
    }

        5、性能再次测试

                       在模拟器上比较   在真机上进行比较

      •   harib10c:   0002638668       0074643595
      •   harib10g:   0004587870      0099969263

        (从上面的测试数据可以看到,精简了程序之后,速度获得了一定的提升)

    harib10h:
      加快中断处理(04):timer_settime()是在中断禁止期间进行的,当面对多任务时,它会托掉很多时间。而且FIFO中用来存储定时器排序后地址的timers[]的排序和跟新也会浪费很多时间;为了解决这两个问题,我们使用链表来重新组织TIMER结构体,在结构体中加入NEXT指向下一个TIMER结构体,这样不用原来的timers[]一直进行排序等工作。
      注---------------意:timer->next指的是下一个定时器的地址;timerctl.next指的是下一个定时器的超时时刻(下图是插入到定时器s和t中间的情况)

        

        1、在TIMER结构体中加入next指向下一个TIMER结构体

    struct TIMER {
        struct TIMER *next;//指向下一个TIMER结构体
        unsigned int timeout, flags;
        struct FIFO32 *fifo;
        int data;
    };

        2、修改定时器的中断处理程序

    void inthandler20(int *esp) {
        int i;
        struct TIMER *timer;
        io_out8(PIC0_OCW2, 0x60);    /* PIC芯片的设置,使能IRQ-00*/
        timerctl.count++;          //count++计数器
        if (timerctl.next > timerctl.count) {
            return;          //count还没有计数到下一个时刻
        }
        timer = timerctl.t0;     /* 获取第一个TIMER结构体地址 */
        for (i = 0; i < timerctl.using; i++) {
            /* 对于每一个运行中的定时器 */
            if (timer->timeout > timerctl.count) {
            //此时退出获得第一个未超时的定时器
                break;
            }
            /* 这个时候超时 了。根据next向后再找,直到找到第一个未超时的 */
            timer->flags = TIMER_FLAGS_ALLOC;
            fifo32_put(timer->fifo, timer->data);
            timer = timer->next;   /* 下一个TIMER的地址 */
        }
        timerctl.using -= i;     //已经有i个超时了
        timerctl.t0 = timer;     //重新设定第一个结构体的地址
    
          /* timerctl.next的设定 */
        if (timerctl.using > 0) {  //有未超时(活动)的定时器
            //获得下一个
            timerctl.next = timerctl.t0->timeout;
        } else {
            //没有活动的定时器,回到初始化状态
            timerctl.next = 0xffffffff;
        }
        return;
    }

        3、修改timer_settime()函数

    void timer_settime(struct TIMER *timer, unsigned int timeout)
    {      //...................
        if (timerctl.using == 1) {
            /*情况一:只有这一个定时器;链表长度为1 */
            timerctl.t0 = timer;       //第一个定时器地址就是这个
            timer->next = 0;          /* 没有下一个定时器了 */
            timerctl.next = timer->timeout;//下一个超时的时刻
            io_store_eflags(e);        //允许中断
            return;
        }
        t = timerctl.t0;           //第一块定时器地址
        if (timer->timeout <= t->timeout) {
            /* 情况二:加入的TIMER超时时刻比第一个TIMER小,加入到链表最前面 */
            timerctl.t0 = timer;      //第一块定时器地址改为新加入的地址
            timer->next = t;         /* 下一个定时器是原来的第一块T */
            timerctl.next = timer->timeout;//跟新下一个超时的时刻
            io_store_eflags(e);       //允许中断
            return;
        }
          /* 情况三:插入到中间位置 */
        for (;;) {
            s = t;
            t = t->next;
            if (t == 0) {
                break;            /* 这是已经没有下一个了 */
            }
            if (timer->timeout <= t->timeout) {
                               /* timer超时时刻在t的前面 */
                s->next = timer;       /* 跟新s的下一个定时器为timer */
                timer->next = t;       /* timer的下一个定时器为t */
                io_store_eflags(e);     //允许中断
                return;
            }
        }
          /* 情况四:插入到最后面 */
        s->next = timer;          //timer直接放到s的后面
        timer->next = 0;          //timer没有下一个
        io_store_eflags(e);        //允许中断
        return;
    }

    harib10i:
      使用“哨兵”简化程序:上一步中,在链表中插入一个TIMER一共有四种情况,下面我们初始化时,在末尾加上一个超时时刻为0xffff_ffff的定时器。让程序简化到两种情况:timer插入到链表头部和timer插入到中间位置。

        1、在init_pit()中加入“哨兵”

    void init_pit(void)
    {    //......
        t = timer_alloc();         /* 分配“哨兵”的内存空间 */
        t->timeout = 0xffffffff;    //“哨兵”超时时刻最大
        t->flags = TIMER_FLAGS_USING;  //哨兵FLAG,活动定时器
        t->next = 0;            /* 没有最后一个,“哨兵”就是最后一个 */
        timerctl.t0 = t;         /* 链表的第一个地址 */
        timerctl.next = 0xffffffff;   /* 下一个超时时刻最大 */
        return;
    }

        2、timer_settime()修改

    void timer_settime(struct TIMER *timer, unsigned int timeout)
    {    //...这里只剩下两种情况 了
        if (timer->timeout <= t->timeout) {
            /* 情况二:插入到最前面 */
            timerctl.t0 = timer;
            timer->next = t; 
            timerctl.next = timer->timeout;
            io_store_eflags(e);
            return;
        }
          /* 情况三:插入到S和T之间 */
        for (;;) {
            s = t;
            t = t->next;
            if (timer->timeout <= t->timeout) {
                s->next = timer; 
                timer->next = t; 
                io_store_eflags(e);
                return;
            }
        }
    }

        3、修改中断程序

    void inthandler20(int *esp)
    {
        struct TIMER *timer;
        io_out8(PIC0_OCW2, 0x60);            /* IRQ-00为PIC的中断口 */
        timerctl.count++;                  //计数器count++
        if (timerctl.next > timerctl.count) {
            return;                    //下一个还没有超时,退出,不进行中断处理
        }
        timer = timerctl.t0;             /* 获得timer的首地址 */
        for (;;) {
            /* timers的flag都为USING */
            if (timer->timeout > timerctl.count) {
                break;                //超时时刻大于当前时刻,没有超时
            }
            /* 超时了 */
            timer->flags = TIMER_FLAGS_ALLOC;  //flag置已分配,调用timer_settime()才是USING 
            fifo32_put(timer->fifo, timer->data);//把数据写到缓冲区
            timer = timer->next;          /* 下一个的地址 */
        }
        timerctl.t0 = timer;            //跟新链表首地址
        timerctl.next = timer->timeout;      //跟新下一个的超时时刻
        return;
    }
  • 相关阅读:
    日记10硬件与操作系统安装专用
    程序设计与算法(三)C++面向对象程序设计 (北大MOOC)
    macbook pro14 前端基本配置【20211114】
    springboot配置rabbitmq的序列化反序列化格式
    sql优化把派生表改成子查询,查询速度将变快
    Android开发 因为ViewPager与SwipeRefreshLayout冲突导致RecyclerView或者其他列表布局的item无法点击的问题
    Kotlin开发 委托
    Kotlin开发 协程的实践 Retrofit + 协程 + ViewModel
    git clean用法
    kotlin开发 高阶函数学习与记录
  • 原文地址:https://www.cnblogs.com/pengfeiz/p/5808133.html
Copyright © 2011-2022 走看看