SmlOS十-系统时钟控制
简介
系统时钟控制也是现代操作系统中,必不可少的功能了。
比如说操作系统的Sleep实现,应用层Timer实现,时钟功能,数据包超时重传等功能,全部都是依赖于系统时钟控制。
那么,操作系统是如何知道时间过了1s钟呢?
这一切都要依赖于一个器件: PIT
PIT简介
PIT (Programmable Interval Timer) 可编程间隔定时器
简要来说就是根据设定,每隔固定周期向cpu发一个中断
比如说:我设定PIT的频率 1/100 秒,也就是说PIT一秒会产生100次时钟中断。
如果我需要知道时间是否过了1s,那么我只要每次发生时钟中断就累加一次,当累加到100的时候就是1s了。
PIT初始化
io_out8(PIT_CTRL, 0x34);
io_out8(PIT_CNT0, 0x9c);
io_out8(PIT_CNT0, 0x2e);
数值的设置需要阅读相关硬件手册。
这里时钟设定中断的频率是每秒执行100次。
初始化完PIT后,就可以收到相关中断了。
时钟中断处理函数,我们之前已经注册,最终跳转到 c实现的inthandler20函数中。
超时管理原理
假如说应用层有非常多的时钟需求,比如有等1s,有等10秒的,
有等100s的,那么系统该如何支持呢?
那么怎么做呢?
首先,每个超时的需求我称之为一个定时器。
系统会维护一个变量,暂时称之为累计时间(count)把,就是每次时钟中断都会自增一,按照现在设定也就是一秒增加100次
假如说有这样的一个情况:
- 1.在count = 0时, 应用层需要一个定时器5s后通知他
-
2.到了count = 100时, 应用层需要一个定时器2s后通知
-
3.到了count = 200时, 应用层需要一个定时器4s后通知
那我要怎么做呢?
有人可能会说,我挨个判断呗,在每次中断时候我都判断下,哪个定时器有没有超时。
如果这样的话,在定时器很多的情况下,系统效率会非常慢。
而且判断操作时放在中断函数里面的,中断函数如果放入很多占时间的操作,那么会可能影响其他中断的产生,所以中断函数需要尽量简短。
所以我们用了一种办法:判断最近超时的定时器
以上面的问题为例子
第一次分配(count = 0,分配5s):
定时器序号 | 超时时间 |
---|---|
1 | 500 (在count = 500的时候通知) |
第二次分配(count = 100,分配2s):
定时器序号 | 超时时间 |
---|---|
2 | 300 |
1 | 500 (在count = 500的时候通知) |
第二次分配(count = 200,分配4s):
定时器序号 | 超时时间 |
---|---|
2 | 300 |
1 | 500 (在count = 500的时候通知) |
3 | 600 |
注意观察:我们在每次分配的时候,都计算出该定时器的预期的超时时间。
且对超时时间进行了排序。
那么我在中断函数中,只要判断最近即将超时的定时器,只需要判断一次,大大增加了效率。
实现
这里介绍两个重要的时钟管理结构体:
struct TIMERCTL
{
unsigned int count, next, using;
struct TIMER *timers[MAX_TIMER];
struct TIMER timers0[MAX_TIMER];
};
count: 累计计数器
next: 下一个超时时间
using: 当前活动的定时器数量
timers: 定时器结构指针数组
(TIMER指针有序数组,排序的时候只移动指针,增加效率)
timers0 定时器结构数组
以及单个时钟结构体:
struct TIMER
{
unsigned int timeout, flags;
struct FIFO8 *fifo;
unsigned char data;
};
timeout: 相对于当前时间的预定时刻
flags: 记录定时器状态
fifo: 定时器队列指针
data: 定时器数据
首先时钟管理表中count成员在每次中断执行后都会自加一,而next是记录下一个即将超时的时钟。
同之前的相关管理表,timers是用来排序的指针数组,而timers0用来分配需要管理的超时时钟。
这里最大时钟数量被指定为500个。
而在单个时钟结构结构体中,timeout是在执行超时设置的操作后,加上count的时间,可以理解为超时时刻。
在时钟中断中一旦发现有时钟超时,那么就向时钟指定的FIFO队列里,写入由data指定的数据,也就是通知该线程。
需要注意的时,每次分配定时器(TIMER)的时候,然后在timers的指针数组中按照timeout的大小进行排序。
相关算法是是进行移位算法,因为是指针的移动,所以移动的总量并非很大。
发表回复
要发表评论,您必须先登录。