《操作系统30天》-合川秀实-学习日志day13
一、简化字符串显示
将类似于这种三步操作:
1)涂上背景色
2)在上面写字符
3)完成刷新
放到一个函数中,如果需要显示字符就直接调用封装好的函数,各个参数代表的含义
这样原来的三行代码就可以简化成(只要需要输出字符,就可以用这个函数替换)
二、重新调整FIFO缓冲区
把定时器用的多个FIFO缓冲区集中成一个,在集装的FIFO中用不同的数据来分辨出是哪个定时器超时。
主函数内的修改:
对if语句的修改:
测试性能:
在进入循环的时候添加count++,完全不显示计数,到了10秒后超时的时候再显示这个count值,在启动3秒的时候把count复位为0。==这里的原理是用定时器计时到10秒输出循环的次数,但是每次要做中断处理(鼠标键盘)都会占用时间,那么循环的次数就会因此变小,越小说明处理的时间越长,==这不是我们想要的。因为我们做的这个操作系统启动(初始化)的时候只要有些条件发生了变化,电脑花费的时间就会不稳定,这就导致开始的时间存在误差,所以统一在3秒后复位,保证时间统一开始。
动了一圈鼠标的效果:
没有动鼠标的效果:
可以发现以上两个结果的count值是不一样的,区别只在于动没动鼠标,我们再用harib10d,harib10e,harib10f…分别测试一下不动鼠标的结果:
根据以上趋势,每次优化之后count的值会变大,count的值越大说明每次的性能变好。
根据定时器可以归纳到一个FIFO缓存区的思想,同理,键盘和鼠标也可以归纳到一个FIFO缓冲区来管理:if (fifo8_status(&keyfifo) + fifo8_status(&mousefifo) + fifo8_status(&timerfifo) == 0)
用写入FIFO的数值来判断中断类型:
没有修改之前的FIFO参数类型为Char,最大只能达到256,对鼠标这种767的数值就不能指定,所以要修改FIFO中的参数类型(修改为int型以前的8位变成32位):
初始化修改(实际上只修改了函数名和参数类型buf):
void fifo32_init(struct FIFO32 *fifo, int size, int *buf) -> FIFO缓冲区的初始化
int fifo32_put(struct FIFO32 *fifo, int data) -> 给FIFO发生数据并储存在FIFO中
int fifo32_get(struct FIFO32 *fifo) -> 从FIFO取得一个数据
int fifo32_status(struct FIFO32 *fifo) ->报告已经存储了多少数据
对keyboard.c和mouse.c中的鼠标和键盘的程序也要做修改:
修改定时器结构体:
Timer.c:
void timer_init(struct TIMER *timer, struct FIFO32 *fifo, int data)
void inthandler20(int *esp)
{
……
for (i = 0; i < timerctl.using; i++) {
/*因为 timers的定时器都处于运行状态,所以不确定flags */
if (timerctl.timers[i]->timeout > timerctl.count) {
break;
}
/* 超时*/
timerctl.timers[i]->flags = TIMER_FLAGS_ALLOC;
fifo32_put(timerctl.timers[i]->fifo, timerctl.timers[i]->data);
}
timerctl.using -= i;
…….
}
最后修改HariMain:
Char s[40]
int fifobuf[128];
(中略)
fifo32_init(&fifo, 128, fifobuf);
init_pit();
init_keyboard(&fifo, 256);
enable_mouse(&fifo, 512, &mdec);
io_out8(PIC0_IMR, 0xf8); /* 设定PIT和PIC1以及键盘为许可(11111000) */
io_out8(PIC1_IMR, 0xef); /* 设定鼠标为许可(11101111) */
timer = timer_alloc();
timer_init(timer, &fifo, 10);
timer_settime(timer, 1000);
timer2 = timer_alloc();
timer_init(timer2, &fifo, 3);
timer_settime(timer2, 300);
timer3 = timer_alloc();
timer_init(timer3, &fifo, 1);
timer_settime(timer3, 50);
for (;;) {
count++;
io_cli();
if (fifo32_status(&fifo) == 0) {
io_sti();
} else {
i = fifo32_get(&fifo);
io_sti();
if (256 <= i && i <= 511) { /*键盘数据 */
sprintf(s, "%02X", i - 256);
putfonts8_asc_sht(sht_back, 0, 16, COL8_FFFFFF, COL8_008484, s, 2);
} else if (512 <= i && i <= 767) { /* 鼠标数据 */
if (mouse_decode(&mdec, i - 512) != 0) {
/* 已经收集了3字节的数据,所以显示出来 */
sprintf(s, "[lcr %4d %4d]", mdec.x, mdec.y);
if ((mdec.btn & 0x01) != 0) {
s[1] = 'L';
}
if ((mdec.btn & 0x02) != 0) {
s[3] = 'R';
}
if ((mdec.btn & 0x04) != 0) {
s[2] = 'C';
}
putfonts8_asc_sht(sht_back, 32, 16, COL8_FFFFFF, COL8_008484, s, 15);
/* 鼠标指针的移动 */
mx += mdec.x;
my += mdec.y;
if (mx < 0) {
mx = 0;
}
if (my < 0) {
my = 0;
}
if (mx > binfo->scrnx - 1) {
mx = binfo->scrnx - 1;
}
if (my > binfo->scrny - 1) {
my = binfo->scrny - 1;
}
sprintf(s, "(%3d, %3d)", mx, my);
putfonts8_asc_sht(sht_back, 0, 0, COL8_FFFFFF, COL8_008484, s, 10);
sheet_slide(sht_mouse, mx, my);
}
} else if (i == 10) { /* 10秒定时器 */
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秒定时器 */
putfonts8_asc_sht(sht_back, 0, 80, COL8_FFFFFF, COL8_008484, "3[sec]", 6);
count = 0; /* 开始测试 */
} else if (i == 1) { /* 光标用定时器 */
timer_init(timer3, &fifo, 0); /* 下面是设定0 */
boxfill8(buf_back, binfo->scrnx, COL8_FFFFFF, 8, 96, 15, 111);
timer_settime(timer3, 50);
sheet_refresh(sht_back, 8, 96, 16, 112);
} else if (i == 0) { /* 光标用定时器 */
timer_init(timer3, &fifo, 1); /* 下面是设定1 */
boxfill8(buf_back, binfo->scrnx, COL8_008484, 8, 96, 15, 111);
timer_settime(timer3, 50);
sheet_refresh(sht_back, 8, 96, 16, 112);
}
}
}
优化完成之后与之前的对比:
Harib10c:
harib10g:
效果明显,优化达到1.85倍左右,作者在真机上测试也得到了改善的效果。这次的改善定时器处理还是一样的原理,但是在主函数里我们修改了只查询一个缓冲区,在没有修改之前我们要多次进行查询,所以这次要加速完成查询,使得“count++”在相同的时间内,执行的次数更多。
三、加快中断处理
- 继续上次没有完成的工作,优化移位处理,定时器的settime是在中断禁止期间进行的,必须要快速完成,如果用移位操作处理多任务(多定时器)的话,时间代价会很高。在FIFO中类此处理方法是:读一个数据之后,改变下一次读数据的地址。不过这个方法不适用于定时器,因为用settime加入中断时,后面的中断必须后移,在中断禁止的情况下,有可能会让下一次读取的定时器发生错误。
- 笔者用链表的方法让每个定时器都保存下一个定时器的地址:
修改定时器的中断处理程序(只改写timers[0]):
修改timer_settime函数:
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();
timerctl.using++;
if (timerctl.using == 1) {
/* 处于运行状态的定时器只有这一个时 */
timerctl.t0 = timer;
timer->next = 0; /*没有下一个 */
timerctl.next = timer->timeout;/*下一次的定时器是这个*/
io_store_eflags(e);
return;
}
t = timerctl.t0;
if (timer->timeout <= t->timeout) {
/* 插入最前面的情况下 */
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) {
/* 插入到s和t之间的时间 */
s->next = timer; /* s的下一个是timer */
timer->next = t; /* timer的下一个是t */
io_store_eflags(e);
return;
}
找到位置后:
}
/* 插入最后面的情况下 */
s->next = timer;
timer->next = 0;
io_store_eflags(e);
return;
}
使用链表的结构之后,timers[]数组已经不需要了,可以直接把最前面的timers[0]定义为t0保留:
在inthandler20和timer_settime中改写timer[0]为t0。
-
使用哨兵简化程序
上面的优化去除了移位处理,但是settime函数还是很长(包含了4种可能实际上可以说成3种因为最后两种的情况可以是一样的):
这次优化减少了四种情况发生的可能,相当于在最大时间处设定了一个定时器称之为“哨兵”,因为无论如何都不可能到达这个时刻0xffffffff:
如果使用需要调整的程序(一年调整一次)必须保证不改变哨兵时刻,哨兵的时刻调整:
加入哨兵之后4种情况变成两种(1,4都不会发生):
修改settime:
精简inthandler20函数:
到了这一步,using就用了,我们可以用哨兵来判断有没有定时器在使用,最后精简inthandler20函数:
最后的效果: