内核定时器:
    unsigned long timeout = jiffies + (x * HZ);
    while(1) {
        // Check the condition.
        // Take a schedule.
        if (time_after(jiffies, timeout)) {
            printk("Timeout\n");
            break;
        }
    }
转换到秒:    
s = (jiffies - last_jiffies)/HZ;
jiffies(约50天溢出)为jiffies_64的后32位,因此直接读取jiffies_64不具备原子性,使用get_jiffies_64,
函数原理:[平台为32位则需要保护读取,否则直接读取]
顺序锁:        读读/读写并发,写写互斥
读写自旋锁:    读读并发,读写/写写互斥
自旋锁:        不允许任何操作并发
u64 get_jiffies_64(void)
{
    unsigned long seq;
    u64 ret;
    do {
        seq = read_seqbegin(&xtime_lock);
        ret = jiffies_64;
    } while (read_seqretry(&xtime_lock, seq)); // 若读的过程中发生写,则重读
    return ret;
}
这里涉及一个顺序锁的读写规则:
读不会更改lock的seq,写会++,这里就会发现到值被写覆盖,于是重新读。

write_seqlock(&lock);
...... //写操作代码
write_sequnlock(&lock);
顺序锁的使用场景是必须默认保持写互斥后,才能使用顺序锁.

睡眠延时:
这种睡眠再调度的精度要低于jiffies的精度:
schedule_timeout(xx);
函数原理:
    expire = timeout + jiffies;            // 超时截至时间
    setup_timer_on_stack(&timer, process_timeout, (unsigned long)current);
    __mod_timer(&timer, expire, false, TIMER_NOT_PINNED);
    schedule();
    del_singleshot_timer_sync(&timer);
    destroy_timer_on_stack(&timer);
    timeout = expire - jiffies;
 out:
    return timeout < 0 ? 0 : timeout;
    
从这里看出,这个函数实际上是基于timer来实现的

标准的延时调度接口 timer_list:[如果需要循环调度,则在timer_func中递归init/add]
struct timer_list my_timer;
init_timer(&my_timer);
my_timer.expire = jiffies + n*HZ;
my_timer.function = timer_func;
my_timer.data = func_parameter;
add_timer(&my_timer);

短延时(基于指令级忙等,不基于jiffy机制的方法):
mdelay/udelay/ndelay
基于一个全局的变量:loops_per_jiffy,变量初始化位于:
calibrate_delay()
基本原理是,先从4096 以*2的倍数找到第一个范围 4096*x < t < 4096*2x
然后逐渐开始细化,从4096*x 开始,逐渐递增4096*x>>2(而不是减半),直到到达对应的精度要求
10000000
11000000
10100000
....

static inline void __udelay(unsigned long usecs)
{
    unsigned long loops_per_usec;
    loops_per_usec = (loops_per_jiffy * HZ) / 1000000;        //一秒中能够执行的指令数目/1000000(ms)
    __delay(usecs * loops_per_usec);                        //Delay 几毫秒的可以执行的指令
}

注:rdtsc这个指令是得到CPU自启动以后的运行周期,不适合超线程和多核CPU

墙上时钟:RTC
static struct timeval curr_time;
do_gettimeofday(&curr_time);

timer_list原理:
初始化timer时,首先取一个cpu变量【timer在哪个cpu注册,就在哪个cpu触发】
作为本cpu上所有timer的控制结构,根据超时程度将timers进行分级管理,其中base->timer_jiffies为最短的那个计时器的时间:
0 - 1<<8       tv1  index = expires
  - 1<<(8+6)   tv2    index = expires >> 8()
  - 1<<(8+2*6) tv3  index = expires >> 8+6()
  - 1<<(8+3*6) tv4  index = expires >> 8+2*6()
  ...          tv5  [不是直接根据索引来决定在数组的地方,因为数组的地方是有限的]

在tv2中,需要把 2^8 - 2^14之间的timers均匀放到一个2^6的数组中,只能2^8对齐,每个数组链中最多放置2^8个timers.
往后类推..

tv1
1  2  3  4  5  6 ....    vec
a1 b1 c1 d1 e1 f1
a2 b2 c2
a3    c3
a4
最后一步,添加timer到目标队列的尾部.

timer_list执行调度:
初始化:init_timers_cpu,主要是分配cpu变量,(除了启动cpu0是固定的静态空间),后再初始化tv1-tv5所有的timer header.
调用:这里从时钟irq中开始执行,增加jiffies.
run_timer_softirq(timer.c)
   ->  __run_timers
   
在函数update_process_times调用run_local_timers后触发软中断:
raise_softirq(TIMER_SOFTIRQ);

遍历过程:
static inline void __run_timers(struct tvec_base *base)
{
    struct timer_list *timer;

spin_lock_irq(&base->lock);
    while (time_after_eq(jiffies, base->timer_jiffies)) {            //遍历所有超时的列表
        struct list_head work_list;
        struct list_head *head = &work_list;
        int index = base->timer_jiffies & TVR_MASK;                    //取其索引

if (!index &&(!cascade(base, &base->tv2, INDEX(0))) &&(!cascade(base, &base->tv3, INDEX(1))) &&!cascade(base, &base->tv4, INDEX(2)))
            cascade(base, &base->tv5, INDEX(3));
        ++base->timer_jiffies;                                        //下一个处理的列表
        list_replace_init(base->tv1.vec + index, &work_list);        //清空这个列表,并处理
        while (!list_empty(head)) {                                    //遍历这个列表下的所有timer
            void (*fn)(unsigned long);
            unsigned long data;
            bool irqsafe;
            timer = list_first_entry(head, struct timer_list,entry);//取出timer
            fn = timer->function;
            data = timer->data;
            irqsafe = tbase_get_irqsafe(timer->base);

timer_stats_account_timer(timer);

base->running_timer = timer;
            detach_expired_timer(timer, base);                        //timer脱链

if (irqsafe) {
                spin_unlock(&base->lock);
                call_timer_fn(timer, fn, data);                        //调用实际的函数
                spin_lock(&base->lock);
            } else {
                spin_unlock_irq(&base->lock);
                call_timer_fn(timer, fn, data);
                spin_lock_irq(&base->lock);
            }
        }
    }
    base->running_timer = NULL;
    spin_unlock_irq(&base->lock);
}

核心的降级处理函数:
#define INDEX(N) ((base->timer_jiffies >> (TVR_BITS + (N) * TVN_BITS)) & TVN_MASK)

static int cascade(struct tvec_base *base, struct tvec *tv, int index)
{
    struct timer_list *timer, *tmp;
    struct list_head tv_list;
    list_replace_init(tv->vec + index, &tv_list);            //获取
    list_for_each_entry_safe(timer, tmp, &tv_list, entry) {
        __internal_add_timer(base, timer);
    }
    return index;
}

index=0 说明当前的tv1已经为空,这个时候base->timer_jiffies应该已经 >256, INDEX(N)的作用就是减去基数获取实际所在的
链表位置,在tv2中timer_jiffies逐渐增加,每次取tv2的一个数组链表然后释放到tv1中(256),逐渐释放,当tv2结束时,同理从tv3
释放到tv2.

linux 定时器原理的更多相关文章

  1. Linux进程调度原理

    Linux进程调度原理 Linux进程调度机制 Linux进程调度的目标 1.高效性:高效意味着在相同的时间下要完成更多的任务.调度程序会被频繁的执行,所以调度程序要尽可能的高效: 2.加强交互性能: ...

  2. Linux 定时器应用【转】

    Linux 定时器应用 实验目的 阅读 Linux 相关源代码,学习 Linux 系统中的时钟和定时器原理,即,ITIMER_REAL实时计数,ITIMER_VIRTUAL 统计进程在用户模式执行的时 ...

  3. 2017-2018-1 20179205《Linux内核原理与设计》第七周作业

    <Linux内核原理与设计>第七周作业 视频学习及操作分析 创建一个新进程在内核中的执行过程 fork.vfork和clone三个系统调用都可以创建一个新进程,而且都是通过调用do_for ...

  4. 2017-2018-1 20179205《Linux内核原理与设计》第四周作业

    <Linux内核原理与分析> 视频学习及实验操作 Linux内核源代码 视频中提到了三个我们要重点专注的目录下的代码,一个是arch目录下的x86,支持不同cpu体系架构的源代码:第二个是 ...

  5. 2017-2018-1 20179209《Linux内核原理与分析》第七周作业

    一.实验 1.1task_struct数据结构 Linux内核通过一个被称为进程描述符的task_struct结构体来管理进程,这个结构体包含了一个进程所需的所有信息.它定义在linux-3.18.6 ...

  6. [转载]Linux进程调度原理

    [转载]Linux进程调度原理 Linux进程调度原理 Linux进程调度的目标 1.高效性:高效意味着在相同的时间下要完成更多的任务.调度程序会被频繁的执行,所以调度程序要尽可能的高效: 2.加强交 ...

  7. JavaScript定时器原理分析

    .header { cursor: pointer } p { margin: 3px 6px } th { background: lightblue; width: 20% } table { t ...

  8. 20169212《Linux内核原理与分析》课程总结

    20169212<Linux内核原理与分析>课程总结 每周作业链接汇总 第一周作业:完成linux基础入门实验,了解一些基础的命令操作. 第二周作业:学习MOOC课程--计算机是如何工作的 ...

  9. linux基础-第十四单元 Linux网络原理及基础设置

    第十四单元 Linux网络原理及基础设置 三种网卡模式图 使用ifconfig命令来维护网络 ifconfig命令的功能 ifconfig命令的用法举例 使用ifup和ifdown命令启动和停止网卡 ...

随机推荐

  1. zabbix系列之九——安装后配置四web监控

    1web监控 描述 详细 备注 概要 1)      需要定义 web 场景(包括一个或多个 HTTP请求),zabbix服务器根据预定义的命令周期性的执行这些步骤. 2)      Web 场景和 ...

  2. 从ibd文件获取表空间id

    xtrabackup恢复过程中出现如下错误 InnoDB: Doing recovery: scanned up to log sequence number ( %) InnoDB: Doing r ...

  3. Oracle EBS OM 取消订单行

    DECLARE l_header_rec OE_ORDER_PUB.Header_Rec_Type; l_line_tbl OE_ORDER_PUB.Line_Tbl_Type; l_action_r ...

  4. 【QT5】 第一个hello world 程序

    #include <QApplication> #include <QWidget> #include <QPushButton> int main(int arg ...

  5. 【转】为什么Github没有记录你的Contributions

    【转】为什么Github没有记录你的Contributions 字数985 阅读0 评论0 喜欢0 记录下为什么github 提交的时候,没有记录到 github 的那个日历上。 Paste_Imag ...

  6. Beta版本项目计划

    小队名称:PHILOSOPHER 小组成员 [组长]金盛昌(201421122043).刘文钊(20142112255).陈笑林(201421122042) 张俊逸(201421122044).陈志建 ...

  7. JAVA-最常用的A题语法

    输出 System.out.println(""); if 语句 if(布尔表达式) { //如果布尔表达式为true将执行的语句 } if...else... 语句 if(布尔表 ...

  8. 布局:上下两个div高度固定,中间自适应

    需求:经典布局 —— 头尾固定高度中间高度自适应布局 头部固定高度,宽度100%自适应父容器: 底部固定高度,宽度100%自适应父容器: 中间是主体部分,自动填满,浏览器可视区域剩余部分,内容超出则中 ...

  9. jQuery 动态加载下拉框选项(Django)

    function change_style() { $.ajax({ url: "{% url 'change_style' %}", type: "GET", ...

  10. 微信支付的notify.php中如何获取订单号(php版)

    不要直接使用demo中的notify.php,重写notify.php,继承WxPayNotify(可参考微信api),具体如下: require_once "WxPay.Api.php&q ...