如何刷网站流量,专门帮做ppt的网站,网站注册怎么做屏蔽过滤,东莞网站建设制作前言
定时器(Timer)对于操作系统非常重要。它在原理上却很简单#xff0c;只是每隔一段时间(比如0.01秒)就发送一个中断信号给CPU。幸亏有了定时器#xff0c;CPU才不用辛苦地去计量时间。……如果没有定时器会怎么样呢?让我们想象一下吧。 假如CPU看不到定时器而仍想计量时…前言
定时器(Timer)对于操作系统非常重要。它在原理上却很简单只是每隔一段时间(比如0.01秒)就发送一个中断信号给CPU。幸亏有了定时器CPU才不用辛苦地去计量时间。……如果没有定时器会怎么样呢?让我们想象一下吧。 假如CPU看不到定时器而仍想计量时间的话就只能牢记每一条指令的执行时间了。比如往寄存器写人常数的MOV指令是1个时钟周期(Clock):加法计算的ADD指令原则上是1个时钟周期但根据条件不同可能是2个时钟周期……等等。CPU不仅要牢记这些内容然后还要据此调查一下调用这些函数所需的时间比如调用这个函数需要150个时钟周期调用那个函数因参数不同需要106到587个时钟周期等。 而这里的“时钟周期”又不是一个固定值。比如CPU主频是100MHz的话一个时钟周期是10 纳秒;但主频如果是200MHz1个时钟周期就是5纳秒。既然CPU有各种主频那么1个时钟周期的时间也就各不相同。大家这下理解cpu的大小核了吧 这样做可以勉强通过程序对时间进行管理实现每隔一定时间进行一次某种处理比如让钟表(程序)的秒针动起来。如果程序中时间计算出错了那么做出的钟表不是快就是慢没法使用。 如果没有定时器,还会出现别的麻烦即不能使用HLT指令。完成这个指令所需的时钟周期不是个固定值。这样一旦执行HLT指令程序就不知道时间了。不能执行HLT指令就意味着要浪费很多电能。所以只能二选一要么放弃时间的计量要么选择浪费电能。左右为难实在糟糕透顶。 打个比方说如果大家没有手表还想知道时间那该怎么办呢?当然不准看太阳也不准看星星。那就只能根据肚子的饥饿程度或者烧一壶开水所用的时间等方法来判断了。 总之只能是一边干点儿什么一边计算时间而且决不能睡觉!一睡觉就没法计时了……就类似这种情况。 然而实际上由于有定时器中断所以不用担心会发生这样的悲剧。程序只需要以自己的步 调处理自己的问题就行了。至于到底经过了多长时间只要在中断处理程序中数一数定时器中断发生的次数就可以了。就算CPU处于HLT状态也可以通过中断来唤醒。根本就没必要让程序自己去记忆时间。CPU也就可以安心地去睡觉了(HT)。这样大家还可以省点电费(笑)。 所以说定时器非常重要。管理定时器是操作系统的重大任务之一,所以在我们的操作系统中也要使用定时器。
一、定时器的设定和使用
1.定时器使用前的设定
要在电脑中管理定时器只需对PIT进行设定就可以了。PIT是“ProgrammableInterval Timer的缩写翻译过来就是“可编程的间隔型定时器”。我们可以通过设定PIT让定时器每隔多少秒就产生一次中断。因为在电脑中PIT连接着IRQ(imteruptrequest参考第6天)的0号所以只要设定了PIT就可以设定IRQ0的中断间隔。……在旧机种上PIT是作为一个独立的芯片安装在主板上的而现在已经和PIC(programmableinterruptcontroller参考第6天)一样被集成到别的芯片里了。 前几天我们学习PIC时曾经非常辛苦从现在开始我们又要重温那种感觉了。大家可不要想:“怎么又学这个?”刚开始学习PIC时陌生的东西比较多学起来很费力。这次就不会那么辛苦了。 电脑里的定时器用的是8254芯片(或其替代品 )那就查一下这个芯片吧。 IRQ0的中断周期变更: ■ AL0x34:OUT(0x43,AL); ■ AL中断周期的低8位;OUT(0x40,AL); ■ AL中断周期的高8位;OUT(0x40,AL); ■ 到这里告一段落。 ■ 如果指定中断周期为0会被看作是指定为65536。实际的中断产生的频率是单位时间时钟周期数(即主频)/设定的数值。比如设定值如果是1000那么中断产生的频率就是1.19318KHz。设定值是10000的话中断产生频率就是119.318Hz。再比如设定值是11932的话中断产生的频率大约就是100Hz了即每10ms发生一次中断。 我们不清楚其中的详细原理只知道只要执行3次OUT指令设定就完成了。将中断周期设定为11932的话中断频率好像就是100Hz也就是说1秒钟会发生100次中断。那么我们就设定成这个值吧。把11932换算成十六进制数就是0x2e9c下面是我们编写的函数initpit。
#define PIT_CTRL 0x0043
#define PIT_CNT0 0x0040void init_pit(void)
{io_out8(PIT_CTRL, 0x34);io_out8(PIT_CNT0, 0x9c);io_out8(PIT_CNT0, 0x2e);return;
}void HariMain(void)
{...init_gdtidt();init_pic();io_sti(); /* IDT/PIC的初始化已经结束所以解除CPU的中断禁止 */fifo8_init(keyfifo, 32, keybuf);fifo8_init(mousefifo, 128, mousebuf);init_pit(); /* 这里 */...
}这样的话IRQ0就会在1秒钟内发生100次中断了。 下面我们来编写IRO0发生时所调用的中断处理程序。它几乎和键盘中断处理程序一样大家还记得吗
void inthandler20(int *esp)
{io_out8(PIC0_OCW2, 0x60); /* 把IRQ-00信号接收完了的信息通知给PIC *//* 暂时什么也不做 */return;
}我们把 init_pit 和 inthandler20 放到了新创建的文件夹 timer.c 下面是naskfunc.nas新增的
_asm_inthandler20:PUSH ESPUSH DSPUSHADMOV EAX,ESPPUSH EAXMOV AX,SSMOV DS,AXMOV ES,AXCALL _inthandler20POP EAXPOPADPOP DSPOP ESIRETD大家还记得下一步是要干什么吗 为了把这个中断处理程序注册到IDTinlt gdtidt函数中也要加上几行。这也和键盘处理的时候差不多哦
/* IDT的设定 */set_gatedesc(idt 0x20, (int) asm_inthandler20, 2 * 8, AR_INTGATE32);set_gatedesc(idt 0x21, (int) asm_inthandler21, 2 * 8, AR_INTGATE32);set_gatedesc(idt 0x27, (int) asm_inthandler27, 2 * 8, AR_INTGATE32);set_gatedesc(idt 0x2c, (int) asm_inthandler2c, 2 * 8, AR_INTGATE32);到这里准备工作就完成了。也不知能不能正常运行。正常的话,嗯,应该什么都不发生。 下面我们执行“make run”。哦什么也没发生。太好了!但这样有点不过瘾还是在中断处理程序中做点什么吧!
2.计量时间
那我们让它干点什么呢?……我们就让它执行下面这段程序吧 bookpack.h
struct TIMERCTL {unsigned int count;
};#include bootpack.h#define PIT_CTRL 0x0043
#define PIT_CNT0 0x0040struct TIMERCTL timerctl;void init_pit(void)
{io_out8(PIT_CTRL, 0x34);io_out8(PIT_CNT0, 0x9c);io_out8(PIT_CNT0, 0x2e);timerctl.count 0; /* 这里 */return;
}void inthandler20(int *esp)
{io_out8(PIC0_OCW2, 0x60); /* 把IRQ-00信号接收完了的信息通知给PIC */timerctl.count; /* 这里 */return;
}大家瞅瞅没几行新增代码。程序所做的处理是:首先定义了struct TIMERCTL结构体。然后在结构体内定义了一个计数变量 count。初始化PIT时将这个计数变量设置为0。每次发生定时器中断时计数变量就以1递增。 也就是说即使这个计数变量在HariMain中不进行加算每1秒钟它也会自动增加100。 为了确认我们把数值显示出来吧。 ...for (;;) {sprintf(s, %010d, timerctl.count);boxfill8(buf_win, 160, COL8_C6C6C6, 40, 28, 119, 43);putfonts8_asc(buf_win, 160, 40, 28, COL8_000000, s);sheet_refresh(sht_win, 40, 28, 120, 44);...这样的话数字应该是以每秒钟100的速度增加。而且不论哪个机种增加速度都是一样的。 即使CPU的速度不同增加速度也应该是一样的。我们先做做看吧。执行“make run。……正常运行了还算顺利。 就能知道从启动开始时间过去了多少秒。如果往方便面里倒入开水的同时利用这个方法就能测量是否到3分钟(180秒)了。哦终于向着有实用价值的操作系统迈出了第一步。
现在从启动开始经过了多少秒这一类问题我们就可以很轻松地判断了。另外我们还可以计量处理所花费的时间。具体做法是处理前看一下时间并把它存放到一个变量里处理结束之后再看一下时间然后只要用减法算出时间差就能得到答案了比如“这个处理耗时13.56秒”等。我们甚至可以据此编制基准测试程序(benchmark program)。 这里大家稍稍回想一下现在已经能够显示出窗口又能使用鼠标又能计量时间还能进行内存管理已经实现了很多功能。有了这些功能只要对它们进行各种组合,就能做很多事情。 我们言归正传继续说定时器吧。操作系统的定时器经常被用于这样一种情形:“喂操作系统过了10秒钟以后通知我一声我要干什么什么”。当然不一定非要是10秒也可以是1秒或30分钟。我们把这样的功能叫做“超时”(timeout)。下面就来实现这个功能吧。 首先往结构体struct TIMERCTL里添加一些代码以便记录有关超时的信息。
struct TIMERCTL {unsigned int count;unsigned int timeout;struct FIFO8 *fifo;unsigned char data;
};以上结构体中的timeout用来记录离超时还有多长时间。一旦这个剩余时间达到0程序就往FIFO缓冲区里发送数据。定时器就是通过这种方法通知HariMain时间到了。至于为什么要使用FIFO缓冲区也说不上个所以然只是觉得这个方法简单因为使用FIFO缓冲区来通知的话可以比照键盘和鼠标利用同样的方法来处理。 下面我们来修改函数吧。
#include bootpack.h#define PIT_CTRL 0x0043
#define PIT_CNT0 0x0040struct TIMERCTL timerctl;void init_pit(void)
{io_out8(PIT_CTRL, 0x34);io_out8(PIT_CNT0, 0x9c);io_out8(PIT_CNT0, 0x2e);timerctl.count 0;timerctl.timeout 0;return;
}void inthandler20(int *esp)
{io_out8(PIC0_OCW2, 0x60); /* 把IRQ-00信号接收结束的信息通知给PIC */timerctl.count;if (timerctl.timeout 0) { /* 如果已经设定了超时 */timerctl.timeout--;if (timerctl.timeout 0) {fifo8_put(timerctl.fifo, timerctl.data);}}return;
}void settimer(unsigned int timeout, struct FIFO8 *fifo, unsigned char data)
{int eflags;eflags io_load_eflags();io_cli();timerctl.timeout timeout;timerctl.fifo fifo;timerctl.data data;io_store_eflags(eflags);return;
}希望大家注意的是我们在inthandler20函数里实现了超时功能。每次发生中断时就把timeout减1减到0时就向fifo发送数据。 在settimer函数里如果设定还没有完全结束IRQ0的中断就进来的话会引起混乱所以我们先禁止中断然后完成设定最后再把中断状态复原。 这在HariMain中如何实现呢?我们来尝试这样做: void HariMain(void)
{struct BOOTINFO *binfo (struct BOOTINFO *) ADR_BOOTINFO;struct FIFO8 timerfifo;char s[40], keybuf[32], mousebuf[128], timerbuf[8];...fifo8_init(timerfifo, 8, timerbuf);settimer(1000, timerfifo, 1);...for (;;) {...io_cli();if (fifo8_status(keyfifo) fifo8_status(mousefifo) fifo8_status(timerfifo) 0) {io_sti();} else {if (fifo8_status(keyfifo) ! 0) {...} else if (fifo8_status(mousefifo) ! 0) {i fifo8_get(mousefifo);io_sti();if (mouse_decode(mdec, i) ! 0) {...} else if (fifo8_status(timerfifo) ! 0) {i fifo8_get(timerfifo); /* 读入 */io_sti();putfonts8_asc(buf_back, binfo-scrnx, 0, 64, COL8_FFFFFF, 10[sec]);sheet_refresh(sht_back, 0, 64, 56, 80);}}}
}程序很简单我们在其中设定10秒钟以后向timerfifo写人“1”这个数据而timerffo接收到数据时就会在屏幕上显示“10[sec]”。 我们执行一下“make run”看显示出来了!
2.设定多个计时器
在上一节做的超时功能超时结束后如果再设定1000的话那我们就可以让它每10秒显示或是让它一闪一灭地显示。另外间隔不仅限于10秒我们还可以设定得更长一些或更短一次些。比如设定为0.5秒的间隔可以用于文字输人时的光标闪烁。 开发操作系统时超时功能非常方便所以在很多地方都可以使用它。比如可以让电子时钟每隔1秒重新显示一次;演奏音乐时可以用它计量音符的长短;也可以让它以0.1秒1次的频率来监视没有中断功能的装置;另外还可以用它实现光标的闪烁功能。 为了简单地实现这些功能我们要准备很多能够设定超时的定时器。 首先把struct TIMERCTL修改成下面这样。
#define MAX_TIMER 500
struct TIMER {unsigned int timeout, flags;struct FIFO8 *fifo;unsigned char data;
};
struct TIMERCTL {unsigned int count;struct TIMER timer[MAX_TIMER];
};这样超时定时器最多就可以设定为500个了fags则用于记录各个定时器的状态。 继续修改对应的函数
#include bootpack.h#define PIT_CTRL 0x0043
#define PIT_CNT0 0x0040struct TIMERCTL timerctl;#define TIMER_FLAGS_ALLOC 1 /* 已配置状态 */
#define TIMER_FLAGS_USING 2 /* 定时器运行中 */void init_pit(void)
{int i;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.timer[i].flags 0; /* 未使用 */}return;
}struct TIMER *timer_alloc(void)
{int i;for (i 0; i MAX_TIMER; i) {if (timerctl.timer[i].flags 0) {timerctl.timer[i].flags TIMER_FLAGS_ALLOC;return timerctl.timer[i];}}return 0; /* 没找到 */
}void timer_free(struct TIMER *timer)
{timer-flags 0; /* 未使用 */return;
}void timer_init(struct TIMER *timer, struct FIFO8 *fifo, unsigned char data)
{timer-fifo fifo;timer-data data;return;
}void timer_settime(struct TIMER *timer, unsigned int timeout)
{timer-timeout timeout;timer-flags TIMER_FLAGS_USING;return;
}void inthandler20(int *esp)
{int i;io_out8(PIC0_OCW2, 0x60); /* 把IRQ-00信号接收结束的信息通知给PIC */timerctl.count;for (i 0; i MAX_TIMER; i) {if (timerctl.timer[i].flags TIMER_FLAGS_USING) {timerctl.timer[i].timeout--;if (timerctl.timer[i].timeout 0) {timerctl.timer[i].flags TIMER_FLAGS_ALLOC;fifo8_put(timerctl.timer[i].fifo, timerctl.timer[i].data);}}}return;
}程序稍微有些长不是很难只要前面的程序大家都明白了这里应该也没什么困难。 最后来看HariMain函数。我们不一定都设定为10秒也尝试一下设为3秒吧。另外我们还要编写类似光标闪烁那样的程序。
void HariMain(void)
{struct BOOTINFO *binfo (struct BOOTINFO *) ADR_BOOTINFO;struct FIFO8 timerfifo, timerfifo2, timerfifo3;char s[40], keybuf[32], mousebuf[128], timerbuf[8], timerbuf2[8], timerbuf3[8];struct TIMER *timer, *timer2, *timer3;...fifo8_init(timerfifo, 8, timerbuf);timer timer_alloc();timer_init(timer, timerfifo, 1);timer_settime(timer, 1000);fifo8_init(timerfifo2, 8, timerbuf2);timer2 timer_alloc();timer_init(timer2, timerfifo2, 1);timer_settime(timer2, 300);fifo8_init(timerfifo3, 8, timerbuf3);timer3 timer_alloc();timer_init(timer3, timerfifo3, 1);timer_settime(timer3, 50);...for (;;) {...io_cli();if (fifo8_status(keyfifo) fifo8_status(mousefifo) fifo8_status(timerfifo) fifo8_status(timerfifo2) fifo8_status(timerfifo3) 0) {io_sti();} else {if (fifo8_status(keyfifo) ! 0) {...} else if (fifo8_status(mousefifo) ! 0) {i fifo8_get(mousefifo);io_sti();if (mouse_decode(mdec, i) ! 0) {} else if (fifo8_status(timerfifo) ! 0) {i fifo8_get(timerfifo); /* 读入 */io_sti();putfonts8_asc(buf_back, binfo-scrnx, 0, 64, COL8_FFFFFF, 10[sec]);sheet_refresh(sht_back, 0, 64, 56, 80);} else if (fifo8_status(timerfifo2) ! 0) {i fifo8_get(timerfifo2); /* 读入 */io_sti();putfonts8_asc(buf_back, binfo-scrnx, 0, 80, COL8_FFFFFF, 3[sec]);sheet_refresh(sht_back, 0, 80, 48, 96);} else if (fifo8_status(timerfifo3) ! 0) {/* 模拟光标 */i fifo8_get(timerfifo3);io_sti();if (i ! 0) {timer_init(timer3, timerfifo3, 0); /* 设置 0 */boxfill8(buf_back, binfo-scrnx, COL8_FFFFFF, 8, 96, 15, 111);} else {timer_init(timer3, timerfifo3, 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);}}}
}下面就是期盼已久的“make run”了。我们执行一下看看我们的成就
二、加快中断处理
1.0
现在我们可以自由使用多个定时器了从数量上说已经足够了。但仔细看一下大家会发现inthandler20还有很大问题:中断处理本来应该在很短的时间内完成,可利用inthandler20时却花费了很长时间。这就妨碍了其他中断处理的执行使得操作系统反应很迟钝。 如果检査inthandler20能发现每次进行定时器中断处理的时候都会对所有活动中的定时器 进行“timerctl.timer[i].timeout–;”处理。也就是说CPU要完成从内存中读取变量值减去1然后又往内存中写人的操作。本来谁也不会注意到这种细微之处但由于我们想在中断处理程序中尽可能减少哪怕是一点点工作量所以才会注意到这里。 问题找到了那该怎么修改才好呢?我们看看下面这样行不行
void inthandler20(int *esp)
{int i;io_out8(PIC0_OCW2, 0x60); /* 把IRQ-00信号接收结束的信息通知给PIC */timerctl.count;for (i 0; i MAX_TIMER; i) {if (timerctl.timer[i].flags TIMER_FLAGS_USING) {if (timerctl.timer[i].timeout timerctl.count) {timerctl.timer[i].flags TIMER_FLAGS_ALLOC;fifo8_put(timerctl.timer[i].fifo, timerctl.timer[i].data);}}}return;
}我们改变了程序中变量timer[i].timeout的含义。它指的不再是“所剩时间”,而是“设定时刻” 了。因为现在的时刻计数到timerctl.count中去了所以就拿它和timer[i].timeout进行比较如果相同或是超过了就通过往FIFO缓冲区里传送数据来通知HariMain。大家现在再看一看我们一直担心的减法计算没有了。这样一改程序的速度应该能稍微变快一些了。 下面我们也要相应地修改timer_settime函数。
void timer_settime(struct TIMER *timer, unsigned int timeout)
{timer-timeout timeout timerctl.count;timer-flags TIMER_FLAGS_USING;return;
}timer_settime函数中所指定的时间是“从现在开始多少多少秒以后”的意思所以用这个 时间加上现在的时刻就可以计算出中断的预定时刻。程序中对这个时刻进行了记录。别的地方就不用改了。
到底这样做行不行呢我们执行一下“make run”。好哇进行得很顺利。虽然还没能切身 感到速度变快了多少不过先自我满足一下吧。 同时也正是因为变成了这种方式在我们这个操作系统中启动以后经过42949673 秒后count就是0xfffffffff了比这个值再大就不能设定了。这么多秒是几天呢?……嗯请稍等(用计算器算一下)……大约是497天。也就是大约一年就要重新启动一次操作系统让count归0。 这里大家可能又会有怨言了“哎呀还需要重新起动这样的操作系统真是麻烦”。事实上 本人也是这么想的(笑)。怎么办才好呢。回到上一节的做法好不好呢?可是回到上 一节的做法速度又有些慢。……既不希望速度慢又不想重新启动 – 为了满足这种奢望我们设计成一年调整一次时刻的程序也许比较好。
int t0timerctl.count;/*所有时刻都要减去这个值*/
io_cli();/*在时刻调整时禁止定时器中断 */
timerctl.count-t0;
for(i0;iMAX TIMER;i){if(timerctl.timeri],flags TIMER FLAGS USING){timerctl.timer[i].timeout t;}
}
io_sti();也许以上方法并非最好但我们不轻言放弃而去想办法解决这种心境是最重要的。只要努 力我们肯定还能找到别的好办法。
2.0
我们再来改善一下吧。
代码如下示例
void inthandler20(int *esp)
{int i;io_out8(PIC0_OCW2, 0x60);timerctl.count;for (i 0; i MAX_TIMER; i) {if (timerctl.timer[i].flags TIMER_FLAGS_USING) {if (timerctl.timer[i].timeout timerctl.count) {timerctl.timer[i].flags TIMER_FLAGS_ALLOC;fifo8_put(timerctl.timer[i].fifo, timerctl.timer[i].data);}}}return;
}如果看一下 harib09e的inthandler20大家会发现每次中断都要执行500次(MAX TIMER的 次数)if语句很浪费时间。由于1秒钟就要发生100次中断这个if语句1秒钟就要执行5万次。 尽管如此这两个if语句都为真而其中的fags值得以更改或者是6fo8put函数能够执行的频 率最多也就是每0.5秒1次即每秒2次左右。其余的49998次if语句都是在做无用功基本没什么意义。
我们来变通一下思考方式如果是人在进行着这样的定时器管理会怎么做呢?定时器加在 一起最多有500个。其中有3秒钟以后超时的有50秒钟以后超时的也有0.3秒钟以后超时的还有一天以后超时的。这种情况下我们首先会关注哪一个?应该是0.3秒钟以后的那个吧。0.3秒钟的结束后下次是3秒钟以后的。也就是没必要把500个都看完只要看到“下一个”的时刻就可以了。因此我们追加一个变量timerctl.next让它记住下一个时刻。
struct TIMERCTL {unsigned int count, next;struct TIMER timer[MAX_TIMER];
};void inthandler20(int *esp)
{int i;io_out8(PIC0_OCW2, 0x60); /* 把IRQ-00信号接收结束的信息通知给PIC */timerctl.count;if (timerctl.next timerctl.count) {return; /* 还不到下一个时刻所以结束 */}timerctl.next 0xffffffff;for (i 0; i MAX_TIMER; i) {if (timerctl.timer[i].flags TIMER_FLAGS_USING) {if (timerctl.timer[i].timeout timerctl.count) {/* 超时 */timerctl.timer[i].flags TIMER_FLAGS_ALLOC;fifo8_put(timerctl.timer[i].fifo, timerctl.timer[i].data);} else {/* 还没有超时的话,这里会找到离下一次超时最近的*/if (timerctl.next timerctl.timer[i].timeout) {timerctl.next timerctl.timer[i].timeout;}}}}return;
}虽然程序变长了,但要做的处理却减少了。在大多数情况下,第一个if语句的return都会执行 中断处理就到此结束了。当到达下一个时刻时使用之前那种方法检查是否超时。超时的话就写人到FIFO中;还没超时的话就调查是否将其设定为下一个时刻(未超时时刻中最小的时刻是下一个时刻)。 如果用这样的方法就能大大减少没有意义的if语句的执行次数速度也应该快多了。 由于使用了next所以其他地方也要修改一下。
void init_pit(void)
{int i;io_out8(PIT_CTRL, 0x34);io_out8(PIT_CNT0, 0x9c);io_out8(PIT_CNT0, 0x2e);timerctl.count 0;timerctl.next 0xffffffff; /* 因为最初没有正在运行的定时器 */for (i 0; i MAX_TIMER; i) {timerctl.timer[i].flags 0; /* 没有使用 */}return;
}void timer_settime(struct TIMER *timer, unsigned int timeout)
{timer-timeout timeout timerctl.count;timer-flags TIMER_FLAGS_USING;if (timerctl.next timer-timeout) {/* 更新下一次的时刻 */timerctl.next timer-timeout;}return;
}这样就好了。现在我们来确认是否能正常运行。“makerun”。……和以前一样虽然仍不能 切身地感受到速度变快了但还是自我满足一下吧。
3.0
到了harib09f的时候中断处理程序的平均处理时间已经大大缩短了。这真是太好了。可是 现在有一个问题那就是到达next时刻和没到next时刻的定时器中断它们的处理时间差别很大。 这样的程序结构不好。因为平常运行一直都很快的程序会偶尔由于中断处理拖得太长而搞得像是主程序要停了似的。更确切一点,这样有时会让人觉得“不知为什么,鼠标偶尔会反应迟钝,很卡。” 因此我们要让到达next时刻的定时器中断的处理时间再缩短一些。怎么办呢?模仿 sheet.c的做法怎么样呢?我们来试试看。 在sheet.c的结构体struct SHTCTL中除了sheet0[]以外我们还定义了*sheets[]。它里面存 放的是按某种顺序排好的图层地址。有了这个变量按顺序描绘图层就简单了。这次我们在stuct TIMERCTL中也定义一个变量其中存放按某种顺序排好的定时器地址。
struct TIMERCTL {unsigned int count, next, using;struct TIMER *timers[MAX_TIMER];struct TIMER timers0[MAX_TIMER];
};变量using相当于struct SHTCTL中的top它用于记录现在的定时器中有几个处于活动中。 改进后的inthandler20函数如下:
void inthandler20(int *esp)
{int i, j;io_out8(PIC0_OCW2, 0x60); /* 把IRQ-00信号接收结束的信息通知给PIC */timerctl.count;if (timerctl.next timerctl.count) {return;}for (i 0; i timerctl.using; i) {/* timers的定时器都处于动作中所以不确认flags */if (timerctl.timers[i]-timeout timerctl.count) {break;}/* 超时 */timerctl.timers[i]-flags TIMER_FLAGS_ALLOC;fifo8_put(timerctl.timers[i]-fifo, timerctl.timers[i]-data);}/* 正好有i个定时器超时了。其余的进行移位。 */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;
}这样即使是在超时的情况下也不用查找下一个next时刻或者查找有没有别的定时器超 时了真不错。如果有很多的定时器都处于正在执行的状态我们会担心定时器因移位而变慢这放在以后再改进吧。 由于timerctl中的变量名改变了所以其他地方也要随之修改。
void init_pit(void)
{int i;io_out8(PIT_CTRL, 0x34);io_out8(PIT_CNT0, 0x9c);io_out8(PIT_CNT0, 0x2e);timerctl.count 0;timerctl.next 0xffffffff; /* 因为最初没有正在运行的定时器 */timerctl.using 0;for (i 0; i MAX_TIMER; i) {timerctl.timers0[i].flags 0; /* 未使用 */}return;
}struct TIMER *timer_alloc(void)
{int i;for (i 0; i MAX_TIMER; i) {if (timerctl.timers0[i].flags 0) {timerctl.timers0[i].flags TIMER_FLAGS_ALLOC;return timerctl.timers0[i];}}return 0; /* 没找到 */
}这两个函数比较简单只是稍稍修改了一下变量名。 在timer_settime函数中必须将timer注册到timers中去而且要注册到正确的位置。如果在注册时发生中断的话可就麻烦了所以我们要事先关闭中断。
void timer_settime(struct TIMER *timer, unsigned int timeout)
{int e, i, j;timer-timeout timeout timerctl.count;timer-flags TIMER_FLAGS_USING;e io_load_eflags();io_cli();/* 搜索注册位置 */for (i 0; i timerctl.using; i) {if (timerctl.timers[i]-timeout timer-timeout) {break;}}/* i号之后全部后移一位 */for (j timerctl.using; j i; j--) {timerctl.timers[j] timerctl.timers[j - 1];}timerctl.using;/* 插入到空位上 */timerctl.timers[i] timer;timerctl.next timerctl.timers[0]-timeout;io_store_eflags(e);return;
}这样做看来不错。虽然中断处理程序速度已经提高了但在设定定时器期间我们关闭了中 这多少有些令人遗憾。不过就算对此不满意也不要随便更改哦。 从某种程度上来讲这也是无法避免的事。如果在设定时多下点工夫整理一下到达中断 时刻时就能轻松一些了。反之如果在设定时偷点懒那么到达中断时刻时就要吃点苦头了。总之要么提前做好准备要么临时抱佛脚。究竟哪种做法好呢要根据情况而定。
总结 现在我们执行“make run”看看吧。希望它能正常运行。会怎么样呢?貌似很顺利太好了。 关于定时器我们还有想要修改的地方。不过大家肯定已经很困了我们还是明天再继续吧。 再见! 文章转载自: http://www.morning.lcjw.cn.gov.cn.lcjw.cn http://www.morning.nssjy.cn.gov.cn.nssjy.cn http://www.morning.kqzxk.cn.gov.cn.kqzxk.cn http://www.morning.gnjtg.cn.gov.cn.gnjtg.cn http://www.morning.kqfdrqb.cn.gov.cn.kqfdrqb.cn http://www.morning.wwznd.cn.gov.cn.wwznd.cn http://www.morning.dshkp.cn.gov.cn.dshkp.cn http://www.morning.myxps.cn.gov.cn.myxps.cn http://www.morning.routalr.cn.gov.cn.routalr.cn http://www.morning.kwwkm.cn.gov.cn.kwwkm.cn http://www.morning.hxrg.cn.gov.cn.hxrg.cn http://www.morning.gthwz.cn.gov.cn.gthwz.cn http://www.morning.mlhfr.cn.gov.cn.mlhfr.cn http://www.morning.dkqbc.cn.gov.cn.dkqbc.cn http://www.morning.tbjtp.cn.gov.cn.tbjtp.cn http://www.morning.ycmpk.cn.gov.cn.ycmpk.cn http://www.morning.mwkwg.cn.gov.cn.mwkwg.cn http://www.morning.hyfrd.cn.gov.cn.hyfrd.cn http://www.morning.zwhtr.cn.gov.cn.zwhtr.cn http://www.morning.kjcll.cn.gov.cn.kjcll.cn http://www.morning.tqldj.cn.gov.cn.tqldj.cn http://www.morning.xkyst.cn.gov.cn.xkyst.cn http://www.morning.pmmrb.cn.gov.cn.pmmrb.cn http://www.morning.zxfr.cn.gov.cn.zxfr.cn http://www.morning.pclgj.cn.gov.cn.pclgj.cn http://www.morning.ntcmrn.cn.gov.cn.ntcmrn.cn http://www.morning.zsthg.cn.gov.cn.zsthg.cn http://www.morning.djbhz.cn.gov.cn.djbhz.cn http://www.morning.jksgy.cn.gov.cn.jksgy.cn http://www.morning.gfrjs.cn.gov.cn.gfrjs.cn http://www.morning.kgxyd.cn.gov.cn.kgxyd.cn http://www.morning.ydwnc.cn.gov.cn.ydwnc.cn http://www.morning.cqrenli.com.gov.cn.cqrenli.com http://www.morning.cftkz.cn.gov.cn.cftkz.cn http://www.morning.3dcb8231.cn.gov.cn.3dcb8231.cn http://www.morning.bwqcx.cn.gov.cn.bwqcx.cn http://www.morning.swkzk.cn.gov.cn.swkzk.cn http://www.morning.qmbpy.cn.gov.cn.qmbpy.cn http://www.morning.yfcbf.cn.gov.cn.yfcbf.cn http://www.morning.kbfzp.cn.gov.cn.kbfzp.cn http://www.morning.uytae.cn.gov.cn.uytae.cn http://www.morning.ygwyt.cn.gov.cn.ygwyt.cn http://www.morning.rbgqn.cn.gov.cn.rbgqn.cn http://www.morning.htbbp.cn.gov.cn.htbbp.cn http://www.morning.mhnb.cn.gov.cn.mhnb.cn http://www.morning.gwsll.cn.gov.cn.gwsll.cn http://www.morning.kwcnf.cn.gov.cn.kwcnf.cn http://www.morning.nyfyq.cn.gov.cn.nyfyq.cn http://www.morning.pfgln.cn.gov.cn.pfgln.cn http://www.morning.hmdn.cn.gov.cn.hmdn.cn http://www.morning.tsnq.cn.gov.cn.tsnq.cn http://www.morning.fwkpp.cn.gov.cn.fwkpp.cn http://www.morning.qncqd.cn.gov.cn.qncqd.cn http://www.morning.jsphr.cn.gov.cn.jsphr.cn http://www.morning.rltw.cn.gov.cn.rltw.cn http://www.morning.kjawz.cn.gov.cn.kjawz.cn http://www.morning.khntd.cn.gov.cn.khntd.cn http://www.morning.nptls.cn.gov.cn.nptls.cn http://www.morning.zfhwm.cn.gov.cn.zfhwm.cn http://www.morning.bpmtg.cn.gov.cn.bpmtg.cn http://www.morning.lpyjq.cn.gov.cn.lpyjq.cn http://www.morning.zzaxr.cn.gov.cn.zzaxr.cn http://www.morning.fbbmg.cn.gov.cn.fbbmg.cn http://www.morning.ygkb.cn.gov.cn.ygkb.cn http://www.morning.brbnc.cn.gov.cn.brbnc.cn http://www.morning.dbqcw.com.gov.cn.dbqcw.com http://www.morning.qzzmc.cn.gov.cn.qzzmc.cn http://www.morning.fwllb.cn.gov.cn.fwllb.cn http://www.morning.tjwfk.cn.gov.cn.tjwfk.cn http://www.morning.cpctr.cn.gov.cn.cpctr.cn http://www.morning.tkchm.cn.gov.cn.tkchm.cn http://www.morning.wklhn.cn.gov.cn.wklhn.cn http://www.morning.kjyfq.cn.gov.cn.kjyfq.cn http://www.morning.hfyll.cn.gov.cn.hfyll.cn http://www.morning.gfmpk.cn.gov.cn.gfmpk.cn http://www.morning.rwxnn.cn.gov.cn.rwxnn.cn http://www.morning.cgtrz.cn.gov.cn.cgtrz.cn http://www.morning.zwdrz.cn.gov.cn.zwdrz.cn http://www.morning.gqbtw.cn.gov.cn.gqbtw.cn http://www.morning.c7629.cn.gov.cn.c7629.cn