理解
- 在叠加图层系统和定时器系统里面可以看到作者的很多优化思路
- 对于这些优化思路,我们都是可以借鉴的
原理
-
通过每隔一定的时间间隔向CPU发送中断,然后我们绑定了该中断所对应的计时函数,即每发生一次中断,该函数加1,并且判断所有的定时器,是否超过了该时间【超过了就需要执行该定时器的操作】,超过了就将定时器里面的数据放入FIFO里面,然后我们在主程序里面判断FIFO里面是否有数据,有数据就取出来,然后根据取出的数据来执行各种预先定义好的操作
-
这个预先定义好的操作里面,我们可以去设置一个同样时间间隔的定时器,循环执行这个操作
-
我们是通过PIT(programmable Interval Timmer) 可编程的间隔型定时器,因为在电脑中PIT连接着IRQ【interrupt request】 0号,所以只要设定了PIT就可以设定IRQ0的中断间隔,PIT是一个独立的芯片,但是现在已经和PIT一样被集成到了别的芯片里面。
-
PIT自身有一些寄存器,这些寄存器本身都是8位的,与端口号相对应,在设置的时候指明对应的端口号进行读写
- IMR是中断屏蔽寄存器,哪一位是1就屏蔽哪一个中断信号
- ICW1和ICW4是硬件相关的ICW3也是固定的
- 只有ICW2是自由配置的,它决定了IRQ以哪一号中断来通知CPU
- CPU收到中断以后,判断可以处理,就会命令PIC发送一个2字节的数据,CPU把数据当做指令执行,该数据是
0x CD ??
,就是INT ??的机器码 - INT 0x20~0x2f接收中断信号IRQ0 ~15而设定,至于不直接用INT 0x00 ~0x0f,是因为这些号码被用来CPU的内部保护中断处理了。
- IDT是中断记录表,他决定了CPU当接收到对应的中断号时,执行哪一个函数。IDT记录了0-255的中断号码与调用函数的对应关系。
- 当CPU执行中断的时候,他需要首先保存当前的工作环境,然后去执行中断函数,最后再还原工作环境,继续当前的程序执行。因此在我们的中断函数里面,首先需要将寄存器的值全部存入栈中,然后调用对应的中断函数,最后再从栈中取出数据放置到寄存器中
- 因此对应的中断函数,都分为两个部分,一是汇编部分,二是C语言部分,汇编部分入栈,调用C语言函数,然后出栈。
定时器优化
- 我们要尽可能的减少CPU处理中断时所消耗的时间,否则会导致卡顿
- 【比如,你正在打字,然后突然计时中断发生了,那么你的打字就会卡在那里,如果这个计时中断绑定的函数执行很快,只有大约0.1s左右,那么你就几乎感受不到这个停顿】
改为判断
- 一开始的操作是每个计时器记录一个时间变量timeout,然后每次中断,对于所有的定时器进行遍历timeout - -,如果该timeout<=0,那么就说明时间到达了,将数据写入FIFO。
- 可是 timerctl.timer[i].timeout-- 这个操作要完成从内存中读取变量值,减去1,然后又往内存中写入的操作。
- 所以我们把timeout改为count+设定时间,意义是从当前时间开始多少秒后,这样就只用执行一个判断语句即可
增加next
- 在定时器中断处理函数当中,有一个for循环遍历定时器,然后有两个if语句,首先判断该定时器是可用的,其次判断是否超时
- 每次中断会执行500次if语句,然后1秒会中断100次,这样导致的结果就是该if语句每秒要执行5万次,【完全可以简化】
- 我们增加一个next变量来指示下一次最接近timeout,因此count只需要和他进行判断即可,如果count<next,直接return,否则就进行遍历,将超时的全部执行,然后继续筛选出新的next,下一个最接近的时间点
- 这样在大部分时间情况下都会return
增加排序数组
- 增加一个存储定时器地址的数组,将数组里面按计时的timeout大小进行排序
- 然后每次count<next的时候,就遍历该有序数组有多少个超时了,然后写入FIFO
- 继而将有序数组后面的定时器往前排。有序数组中存放的都是活跃数组。
- 然后设置定时器的时候,也需要放置到有序数组的合适位置。
- 由于在设定定时器的时候需要禁止中断。
合并FIFO
- 该程序之前分为好几个FIFO缓冲区,在for循环里面,查询缓冲区和count++是交替进行的
- 如果原先需要查询3个缓冲区,现在只需要查询一个,所以会加快count的运行
使用链表
- 以前的程序是使用数组,这样为了维持数组的有序,每次插入新的定时器,都需要重新移动数组元素。
- 当执行计时器中断程序的时候,直接重置头指针即可
- 简单来说,把原来存放活动定时器地址的数组设定成链表的形式。
- 这样当插入新的定时器的时候,会导致有四种情况
- 插入到最前面
- 插入到中间
- 插入到最后面
- 只有一个元素
- 岗哨,我们设置一个时间为0xffff,然后就会一直放置到链表的末尾
- 这样就会消除34两种情况
测试性能
- 在HariMain里面累加Count,查看10s时候的count值,在开机启动3s之后要重置count为0
- 因为电脑在启动时候的偏差会很大,所以在前3s的count值舍弃掉
技巧
- 在FIFO里面取代移位操作,读取一个数据以后不是让后面的数据向前靠齐,而是改变下一次的数据读取地址。但是并不适用于定时器。
多任务
- 对于多任务而言,是有两片内存区域,分别存储着不同的代码片段。
基础版本
- 最开始的版本是这样的,在A程序里面有一个0.1s定时器,到了时间后就执行跳转代码
- 将PC设置为程序B的开始地址,然后程序B开始执行,他同时也设置了一个定时器,到了时间后就会继续跳转到A
升级版本
- 在基础版本里面,只是多任务管理程序嵌入到了运行程序里面,这样不好。
- 升级版本的操作是