4 改善FIFO缓冲区(harib04d)
能不能开发一个不需要数据移送操作的FIFO型缓冲区呢?答案是可以的。因为我们有个技巧可以用。
这个技巧的基本思路是,不仅要维护下一个要写入数据的位置,还要维护下一个要读出数据的位置。这就好像数据读出位置在追着数据写入位置跑一样。这样做就不需要数据移送操作了。数据读出位置追上数据写入位置的时候,就相当于缓冲区为空,没有数据。这种方式很好嘛!
但是这样的缓冲区使用了一段时间以后,下一个数据写入位置会变成31,而这时下一个数据读出位置可能已经是29或30什么的了。当下一个写入位置变成32的时候,就走到死胡同了。因为下面没地方可以写入数据了。
如果当下一个数据写入位置到达缓冲区终点时,数据读出位置也恰好到达缓冲区的终点,也就是说缓冲区正好变空,那还好说。我们只要将下一个数据写入位置和下一个数据读出位置都再置为0就行了,就像转回去从头再来一样。
但是总还是会有数据读出位置没有追上数据写入位置的情况。这时,又不得不进行数据移送操作。原来每次都要进行数据移送,而现在不用每次都做,当然值得高兴,但问题是这样一来,用户会说:“有时候操作系统的反应不好。这系统不行啊。”嗯,我们还是想尽可能避免所有的数据移送操作。
如果将缓冲区扩展到256字节,的确可以减少移位操作的次数,但是不能从根本上解决问题。
■■■■■
仔细想来,当下一个数据写入位置到达缓冲区最末尾时,缓冲区开头部分应该已经变空了(如果还没有变空,说明数据读出跟不上数据写入,只能把部分数据扔掉了)。因此如果下一个数据写入位置到了32以后,就强制性地将它设置为0。这样一来,下一个数据写入位置就跑到了下一个数据读出位置的后面,让人觉得怪怪的。但这无关紧要,没什么问题。
对下一个数据读出位置也做同样的处理,一旦到了32以后,就把它设置为从0开始继续读取数据。这样32字节的缓冲区就能一圈一圈地不停循环,长久使用。数据移送操作一次都不需要。打个比方,这就好像打开一张世界地图,一直向右走的话,会在环绕地球一周后,又从左边出来。这样一来,这个缓冲区虽然只有32字节,可只要不溢出的话,它就能够持续使用下去。
■■■■■
如果不是很理解以上说明的话,可以看看程序,一看就能会明白。
bootpack.h节选
struct KEYBUF { unsigned char data[32]; int next_r, next_w, len; };
变量len是指缓冲区能记录多少字节的数据。
int.c节选
void inthandler21(int *esp) { unsigned char data; io_out8(PIC0_OCW2, 0x61); /* 通知IRQ-01已经受理完毕 */ data = io_in8(PORT_KEYDAT); if (keybuf.len < 32) { keybuf.data[keybuf.next_w] = data; keybuf.len++; keybuf.next_w++; if (keybuf.next_w == 32) { keybuf.next_w = 0; } } return; }
以上无非是将我们的说明写成了程序而已,并没什么难点。倒不如这样说,正是因为看了以上程序,大家才能搞清楚笔者想要说什么。读出数据的程序如下:
for (; ; ) { io_cli(); if (keybuf.len == 0) { io_stihlt(); } else { i = keybuf.data[keybuf.next_r]; keybuf.len——; keybuf.next_r++; if (keybuf.next_r == 32) { keybuf.next_r = 0; } io_sti(); sprintf(s, "%02X", i); boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 0, 16, 15, 31); putfonts8_asc(binfo->vram, binfo->scrnx, 0, 16, COL8_FFFFFF, s); } }
看吧,没有任何数据移送操作。这个缓冲区可以记录大量数据,执行速度又快,真是太棒啦。我们测试一下,运行“make run”,当然能正常运行。耶!