Day13
一些感想与问题
合并缓冲区
果然。。昨天提到的判断多个timer太占地方,这里就开始优化了。
- timer共用一个缓冲区
当定时器超时的时候,定时器只会发送一个1bit的timeout信息。我们可以把它们压缩到一起,这样n个定时器只会占用logn位。 - 所有中断共用一个缓冲区
我们把所有中断产生的向缓冲区存储的信息,分段放入缓冲区中,这个时候8位缓冲区就不那么够用了。改成32位。
0-255:定时器
256-511:键盘输入
512-767:鼠标输入
把不同区间分给不同的中断,检查是否有缓冲区信息的时候,检查对应区间就可以了。
虽然会增加稍许加减法之类的开销,但是比起逐个检查缓冲区来说,这样的开销还是值得的。
链表代替数组
昨天提到的移位太麻烦了,果然,作者就用链表了。
在链表最后,作者为了防止太多的特判情况,加了一个哨兵timmer(该定时器虽然是在运行中,但是永远不会到达)。
测试性能
因为我们有了定时器,就可以测试一段代码的执行效率了。
例如,测试昨天的中断效率是否有改进,就可以在3s的时候开始++count,10s的时候停止count。通过count的多少就可以反映出来程序的运行效率了(count反映了空闲时间)。
至于为什么要3s才开始计时,是因为之前可能会有各种东西的初始化。所以预留3s再计时,可以防止初始化带来的影响。
统一类型的重要性
看到改FIFO的时候,把所以地方的char改成了int,这时候就明白了
typedef unsigned int size_t;
的重要性。
所以为什么windows会把所有东西都typedef了一遍。。
没有用define或者const做到统一修改
观察到在设定偏移的时候,作者在初始化(比如键盘的初始化)的时候写了一遍256,然后在中断判断的时候是
if(256 <= i && i <= 511)
如果能把各种区间提前define好,这样以后修改会方便一些。
今天的代码。。emmm大多是优化。。
简化字符串显示
harib10a
对于打印字符串这个函数,需要背景色+字符串+刷新连着三个函数,所以我们把它们放一起用。
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);
}
重新调整FIFO缓冲区(1)
harib10b
把timer对应上不同的标号,放到缓冲区中。这样就不需要好多个timerfifo了。
测试性能
harib10c
harib10d
harib10e
harib10f
测试性能的方法之前已经说了,于是把昨天不断优化的版本放到这里,确实会有一些略微的优化。
重新调整FIFO缓冲区(2)
harib10g
把char改成了int,于是fifo、各类中断、定义、主函数都需要修改一下。
当所有中断共用一个缓冲区后,系统的执行效率也得到了显著的提升(因为只有一个缓冲区,不会进行过多的查询)。
加快中断处理(4)
harib10h
因为担心移位操作,所以用链表来代替数组好了。
#define MAX_TIMER 500
struct TIMER
{
struct TIMER *next_timer;
unsigned int timeout, flags;
struct FIFO32 *fifo;
int data;
};
struct TIMERCTL
{
unsigned int count, next_time;
struct TIMER *t0; // 其实是链表的头指针
struct TIMER timers0[MAX_TIMER];
};
因为这里还没有用到哨兵,所以先不放其它代码。。
使用“哨兵”简化程序
harib10i
用了哨兵程序,可以简化判断逻辑
有时候我们为了再简化逻辑,可能会把头指针变成一个头结点。但是这里不太适合用头结点,因为头结点的timeout会显得比较逻辑奇怪。
void init_pit()
{
int i;
struct TIMER *t;
io_out8(PIT_CTRL, 0x34);
io_out8(PIT_CNT0, 0x9c);
io_out8(PIT_CNT0, 0x2e);
timerctl.count = 0;
for(i = 0; i < MAX_TIMER; ++i)
{
timerctl.timers0[i].flags = 0; // 未使用
}
t = timer_alloc(); // 取得一个
t->timeout = 0xffffffff;
t->flags = TIMER_FLAGS_USING;
t->next_timer = 0; // 末尾
timerctl.t0 = t; // 因为现在只有哨兵,所以他就在最前面
timerctl.next_time = 0xffffffff; // 因为只有哨兵,所以下一个超时的时刻就是哨兵的时刻
}
void timer_settime(struct TIMER *timer, unsigned int timeout)
{
int e;
struct TIMER *t, *s;
timer->timeout = timeout + timerctl.count;
timer->flags = TIMER_FLAGS_USING;
e = io_load_eflags();
io_cli();
t = timerctl.t0;
if(timer->timeout <= t->timeout)
{
// 插入最前面的情况
timerctl.t0 = timer;
timer->next_timer = t; // 下面是设定t
timerctl.next_time = timer->timeout;
io_store_eflags(e);
return;
}
// 搜寻插入位置
for(;;)
{
s = t;
t = t->next_timer;
if(timer->timeout <= t->timeout)
{
// 插入s和t之间的情况
s->next_timer = timer; // s下一个是timer
timer->next_timer = t; // timer的下一个是t
io_store_eflags(e);
return;
}
}
}
void inthandler20(int *esp)
{
struct TIMER *timer;
io_out8(PIC0_OCW2, 0x60); // 把IRQ-00接收信号结束的信息通知给PIC
timerctl.count++;
if(timerctl.next_time > timerctl.count)
{
return;
}
timer = timerctl.t0; // 首先把最前面的地址赋给timer
for(;;)
{
// 因为timer的定时器都处于运行状态,所以不确认flags
if(timer->timeout > timerctl.count)
{
break;
}
// 超时
timer->flags = TIMER_FLAGS_ALLOC;
fifo32_put(timer->fifo, timer->data);
timer = timer->next_timer; // 将下一定时器的地址代入timer
}
timerctl.t0 = timer;
timerctl.next_time = timer->timeout;
return;
}
这样就算是一个比较好的优化效果了吧。。