操作系统的定时器原理是,操作系统维护了一个定时器节点的链表,新增一个定时器节点时,设置一个jiffies值,这是触发定时中断的频率。linux0.11版本里是1秒触发100次,即10毫秒一次。加入新增一个定时器的jiffies值是2,那经过两次定时中断后就会被执行。jiffies值在每次定时中断时会加一。

_timer_interrupt:push %ds        # save ds,es and put kernel data spacepush %es      # into them. %fs is used by _system_callpush %fspushl %edx      # we save %eax,%ecx,%edx as gcc doesn'tpushl %ecx      # save those across function calls. %ebxpushl %ebx      # is saved as we use that in ret_sys_callpushl %eaxmovl $0x10,%eaxmov %ax,%dsmov %ax,%esmovl $0x17,%eaxmov %ax,%fsincl _jiffies...

下面是定时器的结构图。

#define TIME_REQUESTS 64// 定时器数组,其实是个链表
static struct timer_list {long jiffies;void (*fn)();struct timer_list * next;
} timer_list[TIME_REQUESTS], * next_timer = NULL;void add_timer(long jiffies, void (*fn)(void))
{struct timer_list * p;if (!fn)return;// 关中断,防止多个进程”同时“操作cli();// 直接到期,直接执行回调if (jiffies <= 0)(fn)();else {// 遍历定时器数组,找到一个空项for (p = timer_list ; p < timer_list + TIME_REQUESTS ; p++)if (!p->fn)break;// 没有空项了if (p >= timer_list + TIME_REQUESTS)panic("No more time requests free");// 给空项赋值p->fn = fn;p->jiffies = jiffies;// 在数组中形成链表p->next = next_timer;// next_timer指向第一个节点,即最早到期的next_timer = p;/*修改链表,保证超时时间是从小到大的顺序原理:每个节点都是以前面一个节点的到时时间为坐标,节点里的jiffies即超时时间是前一个节点到期后的多少个jiffies后该节点到期。*/while (p->next && p->next->jiffies < p->jiffies) {// 前面的节点比后面节点大,则前面节点减去后面节点的值,算出偏移值,下面准备置换位置p->jiffies -= p->next->jiffies;// 先保存一下fn = p->fn;// 置换两个节点的回调p->fn = p->next->fn;p->next->fn = fn;jiffies = p->jiffies;// 置换两个节点是超时时间p->jiffies = p->next->jiffies;p->next->jiffies = jiffies;/*到这,第一个节点是最快到期的,还需要更新后续节点的值,其实就是找到一个合适的位置插入,因为内核是用数组实现的定时器队列,所以是通过置换位置实现插入,如果是链表,则直接找到合适的位置,插入即可,所谓合适的位置,就是找到第一个比当前节点大的节点,插入到他前面。*/p = p->next;}/*内核这里实现有个bug,当当前节点是最小时,需要更新原链表中第一个节点的值,,否则会导致原链表中第一个节点的过期时间延长,修复代码如下:if (p->next && p->next->jiffies > p->jiffies) {p->next->jiffies = p->next->jiffies - p->jiffies;}  即更新原链表中第一个节点相对于新的第一个节点的偏移,剩余的节点不需要更新,因为他相对于他前面的节点的偏移不变,但是原链表中的第一个节点之前前面没有节点,所以偏移就是他自己的值,而现在在他前面插入了一个节点,则他的偏移是相对于前面一个节点的偏移*/}sti();
}
// 定时中断处理函数
void do_timer(long cpl)
{extern int beepcount;extern void sysbeepstop(void);if (beepcount)if (!--beepcount)sysbeepstop();// 当前在用户态,增加用户态的执行时间,否则增加该进程的系统执行时间if (cpl)current->utime++;elsecurrent->stime++;// next_timer为空说明还没有定时节点if (next_timer) {// 第一个节点减去一个jiffies,因为其他节点都是相对第一个节点的偏移,所以其他节点的值不需要变next_timer->jiffies--;// 当前节点到期,如果有多个节点超时时间一样,即相对第一个节点偏移是0,则会多次进入while循环while (next_timer && next_timer->jiffies <= 0) {void (*fn)(void);fn = next_timer->fn;next_timer->fn = NULL;// 下一个节点next_timer = next_timer->next;// 执行定时回调函数(fn)();}}if (current_DOR & 0xf0)do_floppy_timer();// 当前进程的可用时间减一,不为0则接着执行,否则可能需要重新调度if ((--current->counter)>0) return;current->counter=0;// 是系统进程则继续执行if (!cpl) return;// 进程调度schedule();
}

操作系统定时器原理分析(基于linux0.11)相关推荐

  1. JavaScript定时器原理分析

    JavaScript中的定时器大家基本在平时的开发中都遇见过吧,但是又有多少人去深入的理解其中的原理呢?下面我们就来分析一下定时器的实现原理. 一.储备知识 在我们在项目中一般会遇见过这样的两种定时器 ...

  2. 对Linux0.11 中 进程0 和 进程1分析

    1. 背景 进程的创建过程无疑是最重要的操作系统处理过程之一,很多书和教材上说的最多的还是一些原理的部分,忽略了很多细节.比如,子进程复制父进程所拥有的资源,或者子进程和父进程共享相同的物理页面,拥有 ...

  3. Linux0.11系统调用之execve流程解析

    Linux0.11系统调用之execve流程解析 前言 execve功能介绍 execve本质 execve系统调用流程 总结 前言 本文是基于Linux0.11源码来叙述该功能,源码可以在oldli ...

  4. 搭建Linux0.11系统环境

    学习Linux0.11内核源代码,我们需要搭建一个基于Linux0.11的操作平台:                     1) boch仿真器:可以从http://bochs.sourceforg ...

  5. linux0.11内核完全剖析 - ll_rw_blk.c

    声明: 参考<linux内核完全剖析基于linux0.11>--赵炯    节选 1.功能描述 该程序主要用于执行低层块设备读 / 写操作,是本章所有块设备与系统其它部分的接口程序.其它程 ...

  6. FastDFS合并存储原理分析

    FastDFS合并存储原理分析 基于FastDFS 5.03/5.04 2014-12-03 一.合并存储简介 在处理海量小文件问题上,文件系统处理性能会受到显著的影响,在读写次数与吞吐量这两个指标上 ...

  7. 嵌入式操作系统多任务调度原理分析与RUST参考实现

    操作系统多任务调度原理分析与RUST参考实现 作为一名在软件领域工程师,在职业生涯的尽头能有幸接触到一部分硬件产品是我莫大的荣幸.秉承我一贯刨根问底,不搞清楚问题本质不罢休的作风和态度,结合基本的计算 ...

  8. 操作系统原理分析实验

    操作系统原理分析要点 https://www.cnblogs.com/huyufeng/p/5400639.html 进程的调度时机与进程切换机制 ------------- 调度时机 中断处理过程( ...

  9. setup.s 分析—— Linux-0.11 学习笔记(二)

    更新记录 版本 时间 修订内容 1.0 2018-4-14 增加了"获取显示模式"这一节,AL取值的表格 标题: setup.s 分析-- Linux-0.11 学习笔记(二) 老 ...

最新文章

  1. html运行代码出现问号乱码_Java 0基础入门(初识Html)
  2. Android 实现瀑布流的两种思路
  3. js和jquery获取父级元素、子级元素、兄弟元素的方法{转}
  4. 堆内存与栈内存能不能共享,不能,,通俗的比较,堆主要用来存放对象的,栈主要是用来执行程序的...
  5. linux怎么编译python_linux 编译安装python3
  6. OpenDRIVE:学习文档
  7. mongovue mysql_mongoVUE的增删改查操作使用说明(转)
  8. 论软件系统建模方法及其应用
  9. 手机app网易邮箱服务器设置,苹果手机iphone怎么设置网易邮箱 iphone设置网易邮箱教程【步骤】...
  10. 《unix环境高级编程》--- 终端I/O
  11. 俄罗斯方块人工智能 [ AI ]
  12. CMD控制台提示“telnet不是内部或外部命令,也不是可运行的程序或批处理文件”
  13. Mock.js有什么用
  14. corners边框_Js实现的6种圆角边框样式
  15. 批处理脚本之批量打开常用软件
  16. 初学C语言:判断输入的数的奇偶性。
  17. 还记得《这个杀手不太冷》里的小女孩吗?电影结束后,她的人生简直像是开了挂一样顺
  18. 太理java题库_2020年Java题库整理
  19. 【生信学习第一天】DEseq2 差异表达基因计算
  20. genetic and generic - a genetic element; generic drugs

热门文章

  1. 迅雷网络中南大学2010年二笔试题
  2. xfire和ajax有哪些特征,XFire使用详解
  3. 进程管理工具supervisor的使用、常见报错问题处理
  4. 超链接标签(外部链接、内部链接、空链接、下载链接、网页元素链接、锚点链接)、注释
  5. 树莓派4 Ubuntu 64位系统 7zip benchmark 跑分
  6. 【node.js从入门到精通】编写接口,使用CROS解决跨域问题,jsonp的接口
  7. 古墓丽影8:地下世界主题包
  8. 杭电oj1000阶乘
  9. 华为荣耀V8手机(EMUI5.0)无法连接电脑MTP模式
  10. 昂瑞微 HS6621 芯片 ADC检测调试