30自制操作系统的第一天12天 计时器(1)

定时器中断处理程序,以确保高效率。它需要一些优化,在这里,优化方法。对于一个操作系统,。有多个计时器。如果操作系统被维持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;
}

上面提到移位,对于定时器少的情况,移位对性能的影响不大。 但对于多任务,非常多应用程序同一时候执行,每一个程序都使用定时器,假设还使用移位的话,就有点浪费时间了。尤其在中断处理程序中进行大量的移位,更是不优雅。

我们用struct TIMER表示一个定时器

struct TIMER {
	struct TIMER *next; // next points to the address of next timer which is going to timeout
	unsigned int timeout, flags; // flags is used to record the status of the timer
	struct FIFO32 *fifo;
	int data;
};

我们的详细做法是,当一个定时器超时时,通过next将下一个要超时的定时器的地址赋给存放全部定时器地址的数组的第一位。那么下次超时出现时,数组的第一位存的就是当前发生超时的定时器的地址。

也就是我们不必再维护一个存有正在使用定时器地址的数组了,仅仅需设一个变量t0用来存储下一个要超时的定时器的地址。全部这些简化都要归功于一開始创建各个定时器时。依据超时时间,用各自的属性next,以链表的方式从早到晚链接起来。设想一下,第一个(也就是最早的)定时器超时了,将它的next赋给t0。那么t0存的就是下一个超时的定时器的地址,依次进行下去。

这样就简化了不少。并且逻辑清晰。

最后再介绍一个程序技巧——”哨兵“。

经过上面的优化后,为了产生上面的线性表。每当一个定时器被定义出来时,都要找准自己的位置。插入到线性表中,这时会有四种情况:

  1. 插入的定时器是第一个定时器
  2. 插入位置在线性表的最前面
  3. 插入位置在中间
  4. 插入位置在线性表最后面

我们每定义一个定时器,都要进行四种情况的推断,有点不优雅。

假设使用了哨兵,会去掉情况1和4。

先来看一下什么是”哨兵“——

在初始化时,将时刻0xffffffff(这个时刻是最大的超时时间)的定时器连到最后一个定时器上,它一直处于后面,仅仅是个附带物,是个留下来看家的留守者,这个留守者正是”哨兵“。

因为有超时时间为0xffffffff的定时器——哨兵存在。所以情况1不会存在,相同,要插入的定时器也不会插在最后,哨兵已经上岗了。这样四种情况的推断缩减到了两种,有利于提升性能。

版权声明:本文博客原创文章。博客,未经同意,不得转载。

时间: 2024-11-08 22:47:31

30自制操作系统的第一天12天 计时器(1)的相关文章

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

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

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

定时器的中断处理程序要保证高效率,需要进行一些优化,这里介绍优化的方法.对于一个操作系统来说,会有多个定时器,假设该操作系统维护了500个定时器,当每一次定时中断发生时(这里我们设定1秒发生100次中断),调用中断处理程序,中断处理程序会对这500个定时器进行if判断,看哪些正在被使用,这样1秒内,就会有500X100=10000次if判断,而中断处理程序最讲究节省时间.实际上,我们不必每发生一次定时中断就去对这500个定时器进行判断.因为假设我们使用了500个定时器中的10个,而10个定时器中

《30自制操作系统》实现中文显示

<30天自制操作系统>最近一直再看,最近已经看到后面了,看到第28天,里面讲到可以实现对全角字符的支持,而原操作系统代码里面只是支持了日语显示,而中文版的这本书也只是讲了一个思路,具体的实现也是没有的.网上也好像没有人实现过这个吧,我是找不到.(由于书中每一章每一小节都有代码,我看书的时候就懒得去实际写代码,就简单看看.不过这次就可以写一下了,加深对这个系统的了解)反正没事做,就准备实现对这个系统的汉字全角支持. 一.了解HZK编码 在改造之前,我们先了解一下符合GB2312标准的中文点阵字库

30天自制操作系统第一天--helloworld

第一天是用二进制编辑器写helloworldos 百度bz.exe这个二进制编辑器下载 在bz里边敲入或复制进十六进制的数据,保存为.img格式 作者是用qemu模拟器运行了这个系统. 我另外用VMware软盘加载这个img文件也能成功显示出hello world 注:01fe处必须是55AA.即第一扇区最后俩字节是55AA

自制操作系统第一周总结

第一个任务是启动. 计算机的启动,参考本篇:http://www.cnblogs.com/rixiang/p/5075825.html 两个重要的概念: 1,boot: 软盘的第一个扇区区为启动区.(计算机读写软盘的时候,不是一个字节一个字节的读取的,而是512字节为一个单位来读写.因此,软盘的512字节为一个扇区.一张软盘的空间共有1440KB,也就是1474560字节,2880个扇区.) 2,IPL: 启动程序加载器.启动区只有区区512字节,实际的操作系统根本装不进去.所以几乎所有的操作系

30岁而立之前成功12条黄金法则【转载】

转自 :http://blog.csdn.net/ricohzhanglong/article/details/4066174#comments 为什么从20岁到30岁的时间,才可以跳两级,但是很多人却在30岁到40岁一下子积累身家,成倍数上涨.因为30岁很重要!譬如,李嘉诚.比尔·盖茨.杨元庆在30岁的时候,都抓住了转折.而他们做的,无非就是认清自己,解决现在和追逐未来.这看上去是人要持续一生解决的问题.但是30岁的人,因为现实的种种情况,面对的问题更突出一些,由此锻炼出的能力也更优异.你的3

Android实战第一篇——时钟+闹钟+计时器+秒表

学习了快一学期的Android了,之前的知识点都是零散的学习的,只有当我们真正的去把他们用起来的时候才会发现难点,自己才会独立尝试去解决某个问题.接下来是我的一个简单的多功能时钟的小实战(视频资源http://pan.baidu.com/s/1i5AT4nN 密码:jq7g) 具体的效果可以参考手机上的时钟. 首先我们来看一看布局文件layout_main.xml 我们总的来看一下整个布局 <FrameLayout xmlns:android="http://schemas.android

“数学口袋精灵”App的第三个Spring计划----开发日记(第一天12.7~第十天12.16)

“数学口袋精灵”第三个Spring计划----第一天 项目进度: 基本完成一个小游戏,游戏具有:随机产生算式,判断对错功能.通过轻快的背景音乐,音效,给玩家提供一个良好的氛围. 任务分配: 冯美欣:设计"数学口袋精灵"App图标.整合修改欢迎界面的背景音乐和游戏界面的背景音乐,防止冲突 吴舒婷:运行测试游戏,需找bug,进一步完善程序 林欢雯:继续完善算法代码的设计,争取消灭bug “数学口袋精灵”第三个Spring计划----第二天 项目进度: 冯美欣:设计好"数序口袋精灵

Garmin eTrex 20/30 自制地图(KMZ)的限制

参考 http://garminbasecamp.wikispaces.com/Custom+Maps 自制地图是KMZ文件,其中打包了jpg图片作为地图显示.限制: More than one JPEG can preside in a KMZ file. The only KML file that will be processed is the doc.kml, inside the KMZ file. Jpegs files are the only supported image t