【操作系统】30天自制操作系统--(12)定时器2

        本章主要是针对上一章【操作系统】30天自制操作系统--(11)定时器1继续进行性能优化的内容。

一 简化字符串显示

本书有个很让人头疼的问题就是,一些本来可以合并在一起形成一个章节的内容,非要零零碎碎地分布在各个章节。这边就是一个典型,一个简简单单地封装操作,前面介绍界面绘制的时候顺便提一下就好了,安排在这边定时器一章多少显得有点不论不类。

        这边就是将下面几个函数:

        合并封装为一个整体putfonts8_asc_sht(意思就是在图层sht中的位置(x,y)处,绘制一个背景色为b,字体色为c,长度为l的字符串s):

二 性能优化1----合并缓冲区FIFO

        现在有5个缓冲区(键盘缓冲区、鼠标缓冲区、3个定时器各申请了一块缓冲区):

        定时器设置上限是500,那岂不是最多要设置500多个缓冲区,而且这些缓冲区大部分时间都是空的,CPU得忙死。所以这边考虑将这些缓冲区共用一块,CPU只要读取一块缓冲区,就可以区分出是键盘、鼠标还是定时器中断就好

        那怎么缓冲区中是哪个中断的数据呢?定时器之间好区分,缓冲区中只存储了定时间隔,10s定时器,那么就是10;3s定时器,那么就是3。从读到的缓冲区数据就可以区分。不同控制设备之间的区分,这边做了如下处理,在读取到的值上面加一个固定的偏移(键盘输入偏移256,鼠标输入偏移512),这样就与其他设备区别开来,先得到判断是哪个中断的数据,而后在处理的过程中,减去对应中断的设定偏移量即可

        这边又出现了一个问题,现在的FIFO8只能操作一个字节的缓存数据(范围是0-255),而当前编号的数据已经最多到了767,所以还需要将之前按一个字节操作的缓冲区数据FIFO8扩展到按四个字节操作的FIFO32。这边道理讲清楚就好办了,具体的改写过程不再罗列,就贴一下最后主函数中的调用:

void HariMain(void)
{
	struct FIFO32 fifo;
	char s[40];
	int fifobuf[128];
    /* 中略 */
	
	fifo32_init(&fifo, 128, fifobuf);
	init_keyboard(&fifo, 256);        /* 键盘缓冲区偏移256 */
	enable_mouse(&fifo, 512, &mdec);  /* 鼠标缓冲区偏移512 */
	timer = timer_alloc();
	timer_init(timer, &fifo, 10);     /* 定时器1缓冲区 */
	timer_settime(timer, 1000);
	timer2 = timer_alloc();
	timer_init(timer2, &fifo, 3);     /* 定时器2缓冲区 */
	timer_settime(timer2, 300);
	timer3 = timer_alloc();
	timer_init(timer3, &fifo, 1);     /* 定时器3缓冲区 */
	timer_settime(timer3, 50);
    /* 中略 */
	

	for (;;) {
		count++;

		io_cli();
		if (fifo32_status(&fifo) == 0) {  /* 原来需要检测多个缓冲区,现在检查fifo一个即可 */
			io_sti();
		} else {
			i = fifo32_get(&fifo);
			io_sti();
			if (256 <= i && i <= 511) {         /* 判断为键盘数据 */
				/* 键盘操作 */
			} else if (512 <= i && i <= 767) {  /* 判断为鼠标数据 */
				/* 鼠标操作 */
			} else if (i == 10) {               /* 判断为10s定时器1 */
				/* 定时器1操作 */
			} else if (i == 3) {                /* 判断为3s定时器2 */
				/* 定时器2操作 */
			} else if (i == 1) {                /* 判断为1s定时器3 */
				/* 定时器3操作 */
			}
            /* 中略 */
		}
	}
}

三 性能优化2----使用链表代替移位管理定时器表

        核心思想就是在结构体struct TIMER中加入next变量,将原来用数组管理的定时器表,改成用线性链表来管理,好处是便于在节点处增删链表元素

         这样,timer_settime函数中创建新的定时器就出现下面几种情况:

        【1】运行中的定时器只有一个;

        【2】插入到最前面的情况;

        【3】插入到两个定时器中间的情况;

        【4】插入到最后面的情况;

        实现如下:

void timer_settime(struct TIMER *timer, unsigned int timeout){
    timer->timeout = timeout + timerctl.count;
    timer->flags = TIMER_FLAGS_USING;
    timerctl.using++;
    struct TIMER *pre, *next;

    int e;
    e = io_load_eflags();
    io_cli();

    if (timerctl.using == 1) {
        /* 【1】处于运行状态的定时器只有这一个的情况 */
        timerctl.t0 = timer;
        timer->next = 0;  /* 没有下一个 */
        timerctl.next = timer->timeout;
        io_store_eflags(e);
        return;
    }
    next = timerctl.t0;
    if (timer->timeout <= next->timeout) {
        /* 【2】插入最前面的情况 */
        timerctl.t0 = timer;
        timer->next = next;
        timerctl.next = timer->timeout;
        io_store_eflags(e);
        return;
    }
    /* 搜寻插入位置 */
    while (1) {
        pre = next;
        next = next->next;
        if (next == 0) {
            break;
        }
        if (timer->timeout <= next->timeout) {
            /* 【3】插入pre和next中间 */
            pre->next = timer;
            timer->next = next;
            io_store_eflags(e);
            return;
        }
    }
    /* 【4】插入最后面的情况 */
    pre->next = timer;
    timer->next = 0;
    io_store_eflags(e);
    return;
}

四 性能优化3----使用“哨兵”机制减少判断分支

        鉴于性能优化3中提出来的四种情况,可以做优化处理,将这四种情况减少到2种,方法是在初始化的时候首先定义一个最大的定时器(定时时长为0xffffffff),通过这种方法,就将上面的情况【1】运行中的定时器只有一个情况【4】插入到最后面两种可能排除,这个初始化的最大定时器就是所谓的“哨兵”(还是挺形象的)。可能情况的减少,带来的就是判断分支的减少以及性能的提升。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值