30天自制操作系统之第12天 定时器

定时器的中断处理程序要保证高效率,需要进行一些优化,这里介绍优化的方法。对于一个操作系统来说,会有多个定时器,假设该操作系统维护了500个定时器,当每一次定时中断发生时(这里我们设定1秒发生100次中断),调用中断处理程序,中断处理程序会对这500个定时器进行if判断,看哪些正在被使用,这样1秒内,就会有500X100=10000次if判断,而中断处理程序最讲究节省时间。实际上,我们不必每发生一次定时中断就去对这500个定时器进行判断。因为假设我们使用了500个定时器中的10个,而10个定时器中最小的超时时间是10s,即10s后才触发第一个定时器,而在这10s内,会进行10000X10=10万次无用的if判断,如果能省去这10万次if判断会是对性能的一次了不起的提升。这里采取的优化策略是:我们维护一个next链,即10个正在被使用的定时器按照超时时间由小到大形成next链,一开始next表示最小的超时时间(本例中为10s),这样每一次调用中断处理程序,先将当前时间count和next进行比较,如果count
< next, 则说明此时没有定时间超时,就不必再对500个定时器进行判断,如果count == next,说明当前正在被使用而且都没有超时的定时器中超时时间最小的定时器到了超时时间,接下来就让这个定时器超时,并选出下一个next,即剩下的正在被使用而且没有超时的定时器中超时时间最小的定时器的超时时间。这样,随着时间的推移,每一次中断,count便加1, count会遇到next链中的各个超时时间,一旦遇到就意味着有定时器要超时了,没有遇到就说明此时没有定时器会超时,就省去了大量的无用的if判断。下面是经过优化的定时中断处理程序:

// when IRQ0(timer interrupt) happens, invoke the interrupt handler: inthandler20
void inthandler20(int *esp)
{
	io_out8(PIC0_OCW2, 0x60); // Inform PIC the information that IRQ0-00 has been received.
	timerctl.count++; // each second it adds 100
	if (timerctl.next > timerctl.count) {
		return; // the next time has not arrived, so finish
	}
	timerctl.next = 0xffffffff;
	int i;
	for (i = 0; i < MAX_TIMER; i++) {
		if (timerctl.timer[i].flags == TIMER_FLAGS_USING) {
			if (timerctl.timer[i].timeout <= timerctl.count) {
				// timeout
				timerctl.timer[i].flags = TIMER_FLAGS_ALLOC;
				fifo8_put(timerctl.timer[i].fifo, timerctl.timer[i].data);
			} else {
				// not timeout
				if (timerctl.next > timerctl.timer[i].timeout) {
					timerctl.next = timerctl.timer[i].timeout; // elect the next time of timeout
				}
			}
		}
	}
	return;
}

其实我们的优化工作并没有结束,在count到达next时刻和没到next时刻的定时中断,它们的处理时间差别很大(没到next,只执行一个判断就return,而到达next,要对500个定时器进行判断)。这样的程序结构不好。因为平常运行一直都很快的程序,会偶尔由于中断处理拖得太长,而搞得像是主程序要停了似的,更确切一点,这样有时会让人觉得“不知为什么,鼠标偶尔会反应迟钝,很卡”,因为此时处理器正忙于定时中断处理程序对500个定时器的if判断。因此我们要让到达next时刻的定时器中断的处理时间再缩短一些。我们再维护一个数组timers,只存储当前正在使用而且未超时的定时器的地址,当到达next时刻,不用再对500个定时器进行判断,只需对timers数组中的定时器进行判断,看究竟是哪个定时器超时了,然后重新对timers数组进行移位,去除刚才超时了的定时器。下面是再次优化后的定时中断处理程序:

// when IRQ0(timer interrupt) happens, invoke the interrupt handler: inthandler20
void inthandler20(int *esp)
{
	int i, j;
	io_out8(PIC0_OCW2, 0x60); // Inform PIC the information that IRQ0-00 has been received.
	timerctl.count++; // each second it adds 100
	if (timerctl.next > timerctl.count) {
		return; // the next time has not arrived, so finish
	}
	// checking in all of the timers which are being used, which timer time out.
	// now we do not have to do MAX_TIMER times of 'if'
	for (i = 0; i < timerctl.using; i++) {
		if (timerctl.timers[i]->timeout > timerctl.count) {
			break;
		}
		// timeout
		timerctl.timers[i]->flags = TIMER_FLAGS_ALLOC;
		fifo8_put(timerctl.timers[i]->fifo, timerctl.timers[i]->data);
	}
	// a timer has timed out, we need shift the rest using timer
	timerctl.using -= i;
	for (j = 0; j < timerctl.using; j++) {
		timerctl.timers[j] = timerctl.timers[i + j];
	}
	if (timerctl.using > 0) {
		timerctl.next = timerctl.timers[0]->timeout;
	} else {
		timerctl.next = 0xffffffff;
	}
	return;
}

30天自制操作系统之第12天 定时器,布布扣,bubuko.com

时间: 2024-12-26 03:25:32

30天自制操作系统之第12天 定时器的相关文章

《30天自制操作系统》笔记(12)——多任务入门

<30天自制操作系统>笔记(12)——多任务入门 进度回顾 上一篇介绍了设置显示器高分辨率的方法.本篇讲一下操作系统实现多任务的方法. 什么是多任务 对程序员来说,也许这是废话,不过还是说清楚比较好. 多任务就是让电脑同时运行多个程序(如一边写代码一边听音乐一边下载电影). 电脑的CPU只有固定有限的那么一个或几个,不可能真的同时运行多个程序.所以就用近似的方式,让多个程序轮换着运行.当轮换速度够快(0.01秒),给人的感觉就是"同时"运行了. 多任务之不实用版 我们首先从

30天自制操作系统之第13天 定时器(2)

这一节我们同样只讲优化工作,关于缓冲区的优化. 我们为键盘.鼠标和定时器各维护了一个FIFO缓冲区,如果定时器有100个,我们要创建100个FIFO缓冲区.这是不优雅的. FIFO缓冲区的作用:拿定时器1来说,我们怎样知道定时器1超时了.假设它的超时时间是10s,那么10s后,定时器1被告知超时,同时往它的FIFO缓冲区写入数据,这样在其他地方,我们只需查看它的FIFO缓冲区是否有数据,就知道它是否发生了超时,如果超时了进行相应的提示工作.其实简单的说,FIFO缓冲区起到了通讯员的作用. 回到我

《30天自制操作系统》读书笔记(2)hello, world

让系统跑起来 要写一个操作系统,我们首先要有一个储存系统的介质,原版书似乎是06年出版的,可惜那时候没有电脑,没想到作者用的还是软盘,现在的电脑谁有软驱?不得已我使用一张128M的SD卡来代替,而事实上你用的是U盘还是软盘对我们的操作系统没有影响,缺点是你的U盘刷入系统后容量只能是1440 MB,即当年流行的3.5英寸软盘的大小,当然不用担心,再格式化一次(用DiskGeniu),就可以恢复. 我做事情的话,总是怕自己的努力的结果白费了,害怕辛辛苦苦看完这本书但是发现做出来的东西现在根本没法用,

《30天自制操作系统》读书笔记(4) 绘图

暑假果然是滋生懒散的温床. (╯‵□′)╯︵┻━┻ 好久不动都忘记之前做到哪里了, 上次好像做到了C语言的引入, 这一节所做的东西都相当轻松, 将会绘制出操作系统的基本界面. 绘图的原理 按照书中所说, 将值写入到显存中就能在屏幕上显示相应的像素, 在asmhead.nas 中有这一段: 1 CYLS EQU 0x0ff0 ; 设定启动区 2 LEDS EQU 0x0ff1 3 VMODE EQU 0x0ff2 ; 关于颜色数目的信息,颜色的位数 4 SCRNX EQU 0x0ff4 ; 分辨率

&lt;&lt;30天自制操作系统&gt;&gt;(1)初体验汇编程序

我们这次使用的汇编语言编译器是原书作者自己开发的,名为“nask”,很多语法和著名的汇编语言编译器nasm很像.由于原书作者没有给出有哪些不同,这里就无法给出不同了! 现在仅仅使用汇编语言中的DB指令来写个“操作系统”吧. DB指令是"define byte"的缩写,往文件里写入1个字节 超长的源代码 1 DB 0xeb, 0x4e, 0x90, 0x48, 0x45, 0x4c, 0x4c, 0x4f 2 DB 0x49, 0x50, 0x4c, 0x00, 0x02, 0x01,

《30天自制操作系统》笔记(08)——叠加窗口刷新

<30天自制操作系统>笔记(08)--叠加窗口刷新 进度回顾 上一篇中介绍了内存管理的思路和算法,我们已经可以动态申请和释放内存了.这不就是堆(Heap)么.在此基础上,本篇要做一段程序,一并解决窗口和鼠标的叠加处理问题. 问题 在之前的<<30天自制操作系统>笔记(05)--启用鼠标键盘>篇,已经能够移动鼠标了.但是遗留了如下图所示的一个小问题. 我们希望的情形是这样的: 实际上,当前版本的OS还没有窗口图层的东西.本篇要做一段程序,一并解决窗口和鼠标的叠加处理问题.

《30天自制操作系统》笔记(01)——hello bitzhuwei’s OS!

<30天自制操作系统>笔记(01)--hello bitzhuwei's OS! 最初的OS代码 1 ; hello-os 2 ; TAB=4 3 4 ORG 0x7c00 ; 指明程序的装载地址 5 6 ; 以下这段是标准FAT32格式软盘专用的代码 7 8 JMP entry 9 DB 0x90 10 DB "HELLOIPL" ; freeparam 启动区的名称可以是任意的字符串(8字节) 11 DW 512 ; 每个扇区(sector)的大小(必须为512字节)

《30天自制操作系统》笔记(04)——显示器256色

<30天自制操作系统>笔记(04)--显示器256色 进度回顾 从最开始的(01)篇到上一篇为止,已经解决了开发环境问题和OS项目的顶层设计问题. 本篇做一个小练习:设置显卡显示256色. 原理 设置显卡模式 调用BIOS中断命令INT 0x10,设置显卡模式为VGA图形模式,320*200*8位彩色模式,调色板模式.代码如下. 1 MOV AL,0x13 ; VGA图形模式,320*200*8位彩色模式 2 MOV AH,0x00 3 INT 0x10 设置调色板 256色的调色板是这样一个

《30天自制操作系统》笔记(02)——导入C语言

<30天自制操作系统>笔记(02)--导入C语言 进度回顾 在上一篇,记录了计算机开机时加载IPL程序(initial program loader,一个nas汇编程序)的情况,包括IPL代码(helloos.nas).编译生成helloos.img文件.用虚拟机QEMU加载helloos.img.制作U盘启动盘和用物理机加载helloos.img. 计算机启动时会自动加载和执行IPL程序,但IPL程序只能占用512字节.若直接用IPL写OS,空间不够用.所以IPL程序一般用于将真正的OS程序