本章主要是针对上一章【操作系统】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】插入到最后面两种可能排除,这个初始化的最大定时器就是所谓的“哨兵”(还是挺形象的)。可能情况的减少,带来的就是判断分支的减少以及性能的提升。