linux kernel 时钟系统的前世今生

趁工作不忙想把最近工作中研究到的kernel的时钟系统 软中断 定时器 tasklet 工作队列实现机制总结下,首先说明,这些原理实现对编写driver不会有多大帮助,但是明白理解这些kernel机制的实现原理,对于我们从系统角度去思考解决问题,会有很大帮助。

上篇博文《一个奇葩bug的解决》就印证了这一点,链接如下:http://blog.csdn.net/skyflying2012/article/details/44623515

那么为什么要把这些内容放在一起总结,因为他们之间是相关联的,kernel的时钟系统和软中断结合实现了定时器,tasklet 工作队列的实现也都是基于软中断的。

内核版本:3.4.55 arm平台

首先把kernel的时钟系统依照我的理解总结下。
时钟系统中最重要的结构体变量:clockevent clocksource xtime,结构体内容不贴了。
clockevent为kernel提供了时钟中断的一些处理函数,特别是对于tickless系统,提供了设置下一次时钟中断时间点的接口set_next_event和timer模式设置接口set_mode。

clocksource则是kernel真正的计数者 时钟源,常规的时钟中断中的计数以及提高精度的补充计数都来自于clocksource。
clocksource成员rating代表了时钟精度,参考值如下:
1--99: 不适合于用作实际的时钟源,只用于启动过程或用于测试; 
100--199:基本可用,可用作真实的时钟源,但不推荐; 
200--299:精度较好,可用作真实的时钟源; 
300--399:很好,精确的时钟源; 
400--499:理想的时钟源,如有可能就必须选择它作为时钟源; 
xtime是kernel的墙上时间,记录从1970-1-1至今的时间差,不管是用户空间还是内核空间要获取的系统时间都需要去读取xtime。

接下来跟随代码,我们来看下kernel时钟系统的前世今生吧。
一 时钟系统的初始化
start_kernel中与时钟系统相关的函数,按照执行先后有:tick_init  timekeeping_init  time_init。

1 tick_init
tick_init注册了与clockevent相关的通知处理函数tick_notify:

static int tick_notify(struct notifier_block *nb, unsigned long reason,
                   void *dev)
{
    switch (reason) {

    case CLOCK_EVT_NOTIFY_ADD:
        return tick_check_new_device(dev);

    case CLOCK_EVT_NOTIFY_BROADCAST_ON:
    case CLOCK_EVT_NOTIFY_BROADCAST_OFF:
    case CLOCK_EVT_NOTIFY_BROADCAST_FORCE:
        tick_broadcast_on_off(reason, dev);
        break;

    case CLOCK_EVT_NOTIFY_BROADCAST_ENTER:
    case CLOCK_EVT_NOTIFY_BROADCAST_EXIT:
        tick_broadcast_oneshot_control(reason);
        break;

    case CLOCK_EVT_NOTIFY_CPU_DYING:
        tick_handover_do_timer(dev);
        break;
<pre name="code" class="plain">    case CLOCK_EVT_NOTIFY_CPU_DEAD:
        tick_shutdown_broadcast_oneshot(dev);
        tick_shutdown_broadcast(dev);
        tick_shutdown(dev);
        break;

    case CLOCK_EVT_NOTIFY_SUSPEND:
        tick_suspend();
        tick_suspend_broadcast();
        break;

    case CLOCK_EVT_NOTIFY_RESUME:
        tick_resume();
        break;

    default:
        break;
    }

    return NOTIFY_OK;
}
 
在后面初始化中我们会用到CLOCK_EVT_NOTIFY_ADD,来添加我们平台相关的clockevent。 

2 timekeeping_init

void __init timekeeping_init(void)
{
    struct clocksource *clock;
    unsigned long flags;
    struct timespec now, boot;

    read_persistent_clock(&now);
    if (!timespec_valid_strict(&now)) {
        pr_warn("WARNING: Persistent clock returned invalid value!\n"
            "         Check your CMOS/BIOS settings.\n");
        now.tv_sec = 0;
        now.tv_nsec = 0;
    }

    read_boot_clock(&boot);
    if (!timespec_valid_strict(&boot)) {
        pr_warn("WARNING: Boot clock returned invalid value!\n"
            "         Check your CMOS/BIOS settings.\n");
        boot.tv_sec = 0;
        boot.tv_nsec = 0;
    }
首先获取外部RTC时间和系统启动时间。这里需要说明下:
kernel运行时系统时间频繁使用,为了提高效率,kernel不会每次获取系统时间时去读外部RTC,访问效率太低,而是在内存中使用xtime来维护系统时间。
RTC单独电池供电不掉电,系统启动中会根据RTC时间同步系统时钟,之后系统时钟根据时钟中断来独立更新运行。

这里read_persistent_clock和read_boot_clock2个平台级函数提供接口来获取外部RTC时间和系统启动时间。

/**
 * read_persistent_clock -  Return time from the persistent clock.
 *
 * Weak dummy function for arches that do not yet support it.
 * Reads the time from the battery backed persistent clock.
 * Returns a timespec with tv_sec=0 and tv_nsec=0 if unsupported.
 *
 *  XXX - Do be sure to remove it once all arches implement it.
 */
void __attribute__((weak)) read_persistent_clock(struct timespec *ts)
{
    ts->tv_sec = 0;
    ts->tv_nsec = 0;
}

/**
 * read_boot_clock -  Return time of the system start.
 *
 * Weak dummy function for arches that do not yet support it.
 * Function to read the exact time the system has been started.
 * Returns a timespec with tv_sec=0 and tv_nsec=0 if unsupported.
 *
 *  XXX - Do be sure to remove it once all arches implement it.
 */
void __attribute__((weak)) read_boot_clock(struct timespec *ts)
{
    ts->tv_sec = 0;
    ts->tv_nsec = 0;
}
这2个函数都是weak类型的,我们可以在平台支持代码中去实现这2个函数来获取RTC时间。但是大部分平台没有实现这2个函数。(可能考虑到外部RTC芯片需在加载driver后初始化才能使用)
而是在加载RTC driver后根据CONFIG_RTC_HCTOSYS内核选项决定是否同步时间。相关代码在driver/rtc/hctsys.c中,这里就不贴了。


接着看timkeeping_init代码。

   seqlock_init(&timekeeper.lock);

    ntp_init();

    write_seqlock_irqsave(&timekeeper.lock, flags);
    clock = clocksource_default_clock();
    if (clock->enable)
        clock->enable(clock);
    timekeeper_setup_internals(clock);

    timekeeper.xtime.tv_sec = now.tv_sec;
    timekeeper.xtime.tv_nsec = now.tv_n
  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值