Linux定时器相关源码分析
Linux的定时器使用时间轮算法。数据结构不难理解,核心数据结构与散列表及其相似,甚至可以说,就是散列表。事实上,理解其散列表的本质,有助于对相关操作的理解。
数据结构
这里先列出一些宏,稍后解释:
#define TVN_BITS (CONFIG_BASE_SMALL ? 4 : 6)
#define TVR_BITS (CONFIG_BASE_SMALL ? 6 : 8)
#define TVN_SIZE (1 << TVN_BITS)
#define TVR_SIZE (1 << TVR_BITS)
#define TVN_MASK (TVN_SIZE - 1)
#define TVR_MASK (TVR_SIZE - 1)
最基本的数据结构称为定时器向量,其实就是一个数组,数组类型就是在linux中不厌其烦用到的双向循环链表。代码如下:
struct tvec {
struct list_head vec[TVN_SIZE];
};
struct tvec_root {
struct list_head vec[TVR_SIZE];
};
示意图如图1:

图1 定时器向量
每个双向队列中timer的expires时间都相同。Linux(32位)中延迟时间用unsign int表示,因此最大为0xFFFFFFFF,如果要表示这么多时间,那数组太大了,肯定是不行的。因此,用了五个这样定时器向量来表示所示,分别为tv1~tv5。tv1是tvec_root类型,tv2~tv5是tvec类型,区别就在于数组大小。前者是TVR_SIZE,后者是TVN_SIZE,即256和64。
根据定时器的到期时间间隔interval=expires-jiffies来决定放入哪一个时间向量。
0~0xff放到tv1,
0x100~0x3fff放到tv2,
0x4000~0xfffff放到tv3,
0x100000~0x3ffffff放到tv4,
0x4000000~0xffffff放到tv5。
各个定时器被组织在一个大时间轮里面:
struct tvec_base {
spinlock_t lock;
struct timer_list *running_timer;
unsigned long timer_jiffies;
unsigned long next_timer;
struct tvec_root tv1;
struct tvec tv2;
struct tvec tv3;
struct tvec tv4;
struct tvec tv5;
} ____cacheline_aligned;
定时器添加
代码如下:
static void internal_add_timer(struct tvec_base *base, struct timer_list *timer)
{
unsigned long expires = timer->expires;
unsigned long idx = expires - base->timer_jiffies;
struct list_head *vec; if (idx < TVR_SIZE) {
int i = expires & TVR_MASK;
vec = base->tv1.vec + i;
} else if (idx < << (TVR_BITS + TVN_BITS)) {
int i = (expires >> TVR_BITS) & TVN_MASK;
vec = base->tv2.vec + i;
} else if (idx < << (TVR_BITS + * TVN_BITS)) {
int i = (expires >> (TVR_BITS + TVN_BITS)) & TVN_MASK;
vec = base->tv3.vec + i;
} else if (idx < << (TVR_BITS + * TVN_BITS)) {
int i = (expires >> (TVR_BITS + * TVN_BITS)) & TVN_MASK;
vec = base->tv4.vec + i;
} else if ((signed long) idx < ) {
/*
* Can happen if you add a timer with expires == jiffies,
* or you set a timer to go off in the past
*/
vec = base->tv1.vec + (base->timer_jiffies & TVR_MASK);
} else {
int i;
/* If the timeout is larger than 0xffffffff on 64-bit
* architectures then we use the maximum timeout:
*/
if (idx > 0xffffffffUL) {
idx = 0xffffffffUL;
expires = idx + base->timer_jiffies;
}
i = (expires >> (TVR_BITS + * TVN_BITS)) & TVN_MASK;
vec = base->tv5.vec + i;
}
/*
* Timers are FIFO:
*/
list_add_tail(&timer->entry, vec);
}
当idx < TVR_SIZE时,显然应当放到tv1中。但是具体放到哪个槽呢?按照我的理解,就应该放到第idx个槽中。事实上不是。因为tv1其实是一个时间轮,有一个时间指针在这个轮上走,这个指针就是timer_jiffies%TVR_SIZE。当timer_jiffies%TVR_SIZE==0时,就走完了一圈。因此应该放到这个指针+idx的位置。而且,如果这个指针在靠近数组的末端,比如254,idx值假设为30,那么254+30=284,显然这是一个相对位置。这里用了一个巧妙的方法来计算,即i=expires&TVR_MASK。由于TVR_MASK=TVR_SIZE-1,而TVR_SIZE=1<<TVR_BITS,在这种情况下expires&TVR_MASK=expires%TVR_SIZE,即求余操作。求余操作解决了相对位置的问题,而位运算则比求余运算更高效。
到了求余就好理解前面说的理解为散列表了。hash函数就是求余函数。
其他
其他问题包括删除定时器,调整定时器等,日后有空再分析。但是有了前面的基础,我想会好理解多了。
Linux定时器相关源码分析的更多相关文章
- linux调度器源码分析 - 运行(四)
本文为原创,转载请注明:http://www.cnblogs.com/tolimit/ 引言 之前的文章已经将调度器的数据结构.初始化.加入进程都进行了分析,这篇文章将主要说明调度器是如何在程序稳定运 ...
- linux调度器源码分析 - 初始化(二)
本文为原创,转载请注明:http://www.cnblogs.com/tolimit/ 引言 上期文章linux调度器源码分析 - 概述(一)已经把调度器相关的数据结构介绍了一遍,本篇着重通过代码说明 ...
- Linux进程调度与源码分析(二)——进程生命周期与task_struct进程结构体
1.进程生命周期 Linux操作系统属于多任务操作系统,系统中的每个进程能够分时复用CPU时间片,通过有效的进程调度策略实现多任务并行执行.而进程在被CPU调度运行,等待CPU资源分配以及等待外部事件 ...
- Netty服务端启动过程相关源码分析
1.Netty 是怎么创建服务端Channel的呢? 我们在使用ServerBootstrap.bind(端口)方法时,最终调用其父类AbstractBootstrap中的doBind方法,相关源码如 ...
- Linux 内核网桥源码分析
Linux网桥源码的实现 转自: Linux二层网络协议 Linux网桥源码的实现 1.调用 在src/net/core/dev.c的软中断函数static void net_rx_action(st ...
- linux调度器源码分析 - 概述(一)
本文为原创,转载请注明:http://www.cnblogs.com/tolimit/ 引言 调度器作为操作系统的核心部件,具有非常重要的意义,其随着linux内核的更新也不断进行着更新.本系列文章通 ...
- Linux进程调度与源码分析(一)——简介
本系列文章主要是近期针对Linux进程调度源码进行阅读与分析后的经验总结,分析过程中可能结合部分Linux网络编程的相关知识以便于理解,加深对Linux进程调度的理解和知识分享. 本系列文章主要结合L ...
- Linux内核 fork 源码分析
内核版本:linux-4.4.18 源码位置:这里 fork相关的代码最终执行的函数为_do_fork(),下面按照顺序分析下_do_fork(): 首先判断是否需要trace(跟踪)这个进程,这一步 ...
- Cocos2d-X3.0 刨根问底(八)----- 场景(Scene)、层(Layer)相关源码分析
本章节我们重点分析Cocos2d-x3.0与 场景.层相关的源码.这部分源码集中在 libcocos2d –> layers_scenes_transitions_nodes目录下面 我先发个截 ...
随机推荐
- 为您详细比较三个 CSS 预处理器(框架):Sass、LESS 和 Stylus
CSS 预处理器技术已经非常的成熟,而且也涌现出了越来越多的 CSS 的预处理器框架.本文向你介绍使用最为普遍的三款 CSS 预处理器框架,分别是 Sass.Less CSS.Stylus. 首先我们 ...
- Dynamics AX 2012 R3 Demo 安装与配置 - 安装数据服务器、AOS和客户端 (Step 2)
上一节中,Reinhard主要讲解了怎么配置安装环境,尤其是域控制器,并在域中添加了一个管理员账户 MSDynAX.NET\Reinhard ,以后的安装配置,均在该账户下进行. 现在运行 AX 20 ...
- Linux系统使用yum安装nodejs
先执行: yum install \ http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm 再执行: su ...
- EntityFramework Core 学习笔记 —— 创建模型
原文地址:https://docs.efproject.net/en/latest/modeling/index.html 前言: EntityFramework 使用一系列的约定来从我们的实体类细节 ...
- TortoiseGit 连接每次都要输入用户名和密码
当你配置好git后,在C:\Documents and Settings\Administrator\ 或者 C:\Users\Administrator 目录下有一个 .gitconfig 的文件 ...
- Linux命令帮助中尖括号<>和方括号[]以及省略号...的含义
在方括号内的表达式("[" 和 "]"之间的字符)是可选的(写命令时要去掉括号). 在尖括号内的表达式("[" 和 "]" ...
- [问题2014A02] 解答三(降阶公式法)
[问题2014A02] 解答三(降阶公式法) 将矩阵 \(A\) 写成如下形式: \[A=\begin{pmatrix} -2a_1 & 0 & \cdots & 0 & ...
- Web的Ajax应用开发模式(二)——Ajax开发模式分析
寄语: 前天在查看一些公司的招聘要求时,看到有公司要求测试人员了解Ajax,故写此博文旨在帮助测试人员提高自身技术知识水平,愿与广大测试同胞共同进步.(欢迎纠错!!!) Web应用的传统开发模式总结: ...
- InfoObject共享
声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...
- ExceptionExtensions
public static class ExceptionExtensions { public static IEnumerable<Exception> GetAllException ...