30天自制操作系统 定时器

本文探讨了定时器系统中的优化策略,如通过PIT实现精确计时,改进定时器处理逻辑减少CPU中断开销,采用链表提高数据处理效率,以及在多任务环境中优化定时器管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

理解
  • 在叠加图层系统和定时器系统里面可以看到作者的很多优化思路
  • 对于这些优化思路,我们都是可以借鉴的
原理
  • 通过每隔一定的时间间隔向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的运行
使用链表
  • 以前的程序是使用数组,这样为了维持数组的有序,每次插入新的定时器,都需要重新移动数组元素。
  • 当执行计时器中断程序的时候,直接重置头指针即可
  • 简单来说,把原来存放活动定时器地址的数组设定成链表的形式。
  • 这样当插入新的定时器的时候,会导致有四种情况
    1. 插入到最前面
    2. 插入到中间
    3. 插入到最后面
    4. 只有一个元素
  • 岗哨,我们设置一个时间为0xffff,然后就会一直放置到链表的末尾
  • 这样就会消除34两种情况
测试性能
  • 在HariMain里面累加Count,查看10s时候的count值,在开机启动3s之后要重置count为0
  • 因为电脑在启动时候的偏差会很大,所以在前3s的count值舍弃掉
技巧
  • 在FIFO里面取代移位操作,读取一个数据以后不是让后面的数据向前靠齐,而是改变下一次的数据读取地址。但是并不适用于定时器。
多任务
  • 对于多任务而言,是有两片内存区域,分别存储着不同的代码片段。
基础版本
  • 最开始的版本是这样的,在A程序里面有一个0.1s定时器,到了时间后就执行跳转代码
  • 将PC设置为程序B的开始地址,然后程序B开始执行,他同时也设置了一个定时器,到了时间后就会继续跳转到A
升级版本
  • 在基础版本里面,只是多任务管理程序嵌入到了运行程序里面,这样不好。
  • 升级版本的操作是
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值