# Timers and time management in the Linux kernel. Part 6
Date: 2020-04-27


这篇文章 [Timers and time management in the Linux kernel. Part 6.](https://0xax.gitbooks.io/linux-insides/Timers/linux-timers-6.html) 是出自 [linux-insides](https://0xax.gitbooks.io/linux-insides/content/)一书中 [Timers and time management](https://github.com/0xAX/linux-insides/tree/master/Timers) 章节

> _内核版本比对5.7-rc1 进行了相关调整, 增加相关备注_

# Linux内核中的定时器和时间管理.Part 6.

## x86\_64 相关的时钟源

这是[chapter](https://0xax.gitbooks.io/linux-insides/content/Timers/index.html)的第六部分，它描述了Linux内核中与计时器和时间管理相关的内容。 在之前的[part](https://0xax.gitbooks.io/linux-insides/content/Timers/linux-timers-5.html)中，我们看到了`clockevents`框架，现在我们将继续深入探讨与时间管理相关的问题 Linux内核中的内容。 本部分将描述与时钟源相关的[x86](https://en.wikipedia.org/wiki/X86)架构的实现（有关\[clocksource\]概念的更多信息，您可以在[second part](https://0xax.gitbooks.io/linux-insides/content/Timers/linux-timers-2.html) 中找到相关信息.

首先，我们必须知道在x86体系结构中可以使用哪些时钟源。 从 [sysfs](https://en.wikipedia.org/wiki/Sysfs) 或者从下面的文件 `/sys/devices/system/clocksource/clocksource0/available_clocksource`. `/sys/devices/system/clocksource/clocksourceN`来获取相关信息:

- `available_clocksource` - 提供系统可用是时钟源
- `current_clocksource` - 提供系统当前使用时钟源

实际看一下:

```C
$ cat /sys/devices/system/clocksource/clocksource0/available_clocksource 
tsc hpet acpi_pm 
```

我们可以看到有三个注册的时钟源在这个系统里：

- `tsc` - [Time Stamp Counter](https://en.wikipedia.org/wiki/Time_Stamp_Counter);
- `hpet` - [High Precision Event Timer](https://en.wikipedia.org/wiki/High_Precision_Event_Timer);
- `acpi_pm` - [ACPI Power Management Timer](http://uefi.org/sites/default/files/resources/ACPI_5.pdf).

现在让我们看看第二个文件，它提供了最佳时钟源(在系统中具有最佳评级的时钟源)

```C
$ cat /sys/devices/system/clocksource/clocksource0/current_clocksource 
tsc
```

`tsc`是[Time Stamp Counter](https://en.wikipedia.org/wiki/Time_Stamp_Counter)的简写. [second part](https://0xax.gitbooks.io/linux-insides/content/Timers/linux-timers-2.html)有过描述, 他描述了Linux Kernel的`clocksource`框架, 系统最好的时钟源应当是最有最好或最高功率,频率的, 或者说是具有最高[frequency](https://en.wikipedia.org/wiki/Frequency).

[ACPI](https://en.wikipedia.org/wiki/Advanced_Configuration_and_Power_Interface)电源管理计时器的频率为`3.579545` MHz。 [High Precision Event Timer](https://en.wikipedia.org/wiki/High_Precision_Event_Timer)的频率至少为`10 MHz`。 [Time Stamp Counter](https://en.wikipedia.org/wiki/Time_Stamp_Counter)的频率取决于处理器。 例如，在较旧的处理器上，`Time Stamp Counter` 通过内部处理器时钟周期进行计数。 这意味着当处理器的频率缩放比例改变时，其频率也会改变。 对于较新的处理器，情况已经改变。 较新的处理器具有一个`invariant Time Stamp counter`，在处理器的所有运行状态下均以恒定速率递增。 实际上，我们可以在`/proc/cpuinfo`的输出中获得它的频率。 例如，对于系统中的第一个处理器：

```C
$ cat /proc/cpuinfo
...
model name : Intel(R) Core(TM) i7-4790K CPU @ 4.00GHz
...
```

尽管英特尔手册说时间戳计数器的频率, 虽然恒定，但不一定是处理器的最大合格频率，也不一定是`CPU Brand String`中给出的频率，但是无论如何，我们可能会发现它远远超过了`ACPI PM`或 `High Precision Event Timer`的频率。 我们可以看到，额定值最高或频率最高的时钟源是系统当前的值。

您可以注意到，除了这三个时钟源之外，我们在`/sys/devices/system/clocksource/clocksource0/available_clocksource`的输出中看不到另外两个我们熟悉的时钟源。 这些时钟源是`jiffy`和`refined_jiffies`。我们看不到它们，因为此字段仅映射高分辨率时钟源，或者换句话说，带有 [CLOCK\_SOURCE\_VALID\_FOR\_HRES](https://github.com/torvalds/linux/blob/master/include/linux/clocksource.h#L113) 标识的时钟源。

正如我上面已经写过的，我们将在本部分中考虑所有这三个时钟源。 我们将按照它们的初始化顺序进行考虑，或者：

- `hpet`;
- `acpi_pm`;
- `tsc`.

通过[dmesg](https://en.wikipedia.org/wiki/Dmesg) 我们可以看到输出顺序和上面一致:

```C
$ dmesg | grep clocksource
[    0.000000] clocksource: refined-jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 1910969940391419 ns
[    0.000000] clocksource: hpet: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 133484882848 ns
[    0.094369] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 1911260446275000 ns
[    0.186498] clocksource: Switched to clocksource hpet
[    0.196827] clocksource: acpi_pm: mask: 0xffffff max_cycles: 0xffffff, max_idle_ns: 2085701024 ns
[    1.413685] tsc: Refined TSC clocksource calibration: 3999.981 MHz
[    1.413688] clocksource: tsc: mask: 0xffffffffffffffff max_cycles: 0x73509721780, max_idle_ns: 881591102108 ns
[    2.413748] clocksource: Switched to clocksource tsc
```

第一个时钟源 [High Precision Event Timer](https://en.wikipedia.org/wiki/High_Precision_Event_Timer), 下面先开始介绍它.

## High Precision Event Timer

[x86](https://en.wikipedia.org/wiki/X86)架构的`High Precision Event Timer`的实现位于 [arch/x86/kernel/hpet.c](https://github.com/torvalds/linux/blob/master/arch/x86/kernel/hpet.c)源代码文件中。 它的初始化从`hpet_enable`函数的调用开始。 在Linux内核初始化期间调用此函数。 如果我们从 [init/main.c](https://github.com/torvalds/linux/blob/master/init/main.c)源代码文件中查看`start_kernel`函数，则会看到在初始化所有特定于体系结构的东西之后，禁用了早期控制台，并且时间管理子系统已经准备就绪，请调用以下函数：

```C
if (late_time_init)
    late_time_init();
```

在早期的jiffy计数器已经初始化之后，它将对后期体系结构特定的计时器进行初始化。 x86体系结构的`late_time_init`函数的定义位于 [arch/x86/kernel/time.c](https://github.com/torvalds/linux/blob/master/arch/x86/kernel/time.c)。 看起来很简单:

```C
static __init void x86_late_time_init(void)
{
    /*
     * Before PIT/HPET init, select the interrupt mode. This is required
     * to make the decision whether PIT should be initialized correct.
     */
    x86_init.irqs.intr_mode_select();

    /* Setup the legacy timers */
    x86_init.timers.timer_init();

    /*
     * After PIT/HPET timers init, set up the final interrupt mode for
     * delivering IRQs.
     */
    x86_init.irqs.intr_mode_init();
    tsc_init();
}

```

> _对于通过`8253`芯片提供的`PIT`时钟源进行优化，在不支持acpi情况下都跳过PIT的初始化_ _[x86/timer: Skip PIT initialization on modern chipsets](https://github.com/torvalds/linux/commit/c8c4076723daca08bf35ccd68f22ea1c6219e207)_ _[x86/timer: Don't skip PIT setup when APIC is disabled or in legacy mode](https://github.com/torvalds/linux/commit/979923871f69a4dc926658f9f9a1a4c1bde57552)_

正如我们看到的那样，它完成了`x86`相关计时器的初始化和`Time Stamp Counter`的初始化。 在下一段中将看到的秒数，但是现在让我们考虑`x86_init.timers.timer_init`函数的调用。 `timer_init`指向同一源代码文件中的hpet\_time\_init函数。 我们可以通过查看 [arch/x86/kernel/x86\_init.c](https://github.com/torvalds/linux/blob/master/arch/x86/kernel/x86_init.c) 的`x86_init`结构的定义来验证这一点：

```C
struct x86_init_ops x86_init __initdata = {
   ...
   ...
   ...
   .timers = {
        .setup_percpu_clockev   = setup_boot_APIC_clock,
        .timer_init     = hpet_time_init,
        .wallclock_init     = x86_init_noop,
   },
   ...
   ...
   ...
```

如果我们不能启用`High Precision Event Timer` ，`hpet_time_init`可以设置[programmable interval timer](https://en.wikipedia.org/wiki/Programmable_interval_timer) ，并为启用的计时器设置默认计时器IRQ

```C
void __init hpet_time_init(void)
{
    if (!hpet_enable()) {
        if (!pit_timer_init())
            return;
    }
    setup_default_timer_irq();
}
```

首先，通过`hpet_enable`函数通过调用`is_hpet_capable`函数, 检查我们是否能够在系统中启用`高精度事件计时器`，如果可以，我们为其映射一个虚拟地址空间：

```C
int __init hpet_enable(void)
{
    if (!is_hpet_capable())
        return 0;

    hpet_set_mapping();
}
```

`is_hpet_capable` 检查是否在kernel命令行设置`hpet=disable`,`hpet_address` 从 [ACPI HPET](https://en.wikipedia.org/wiki/Advanced_Configuration_and_Power_Interface) 表中获取. `hpet_set_mapping` 函数为时间寄存器映射虚拟地址空间:

```C
static inline void hpet_set_mapping(void)
{
    hpet_virt_address = ioremap(hpet_address, HPET_MMAP_SIZE);
}
```

> /arch/x86/kernel/hpet.c

可以参考文章 [IA-PC HPET (High Precision Event Timers) Specification](http://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/software-developers-hpet-spec-1-0a.pdf):

> Timer寄存器空间1024字节

因此 `HPET_MMAP_SIZE` 是 `1024` 字节:

```C
#define HPET_MMAP_SIZE     1024
```

> /arch/x86/include/asm/hpet.h

`High Precision Event Timer` 获取虚拟空间后，可以通过读取`HPET_ID`获取相关的值:

```C
id = hpet_readl(HPET_ID);

last = (id & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT;
```

我们需要获取此数字，以便为`HPET`的`General Configuration Register`分配正确的空间量：

```C
cfg = hpet_readl(HPET_CFG);

hpet_boot_cfg = kmalloc((last + 2) * sizeof(*hpet_boot_cfg), GFP_KERNEL);
```

在为`HPET`的配置寄存器分配空间之后，我们允许主计数器运行，并允许定时器中断(如果通过将所有定时器的配置寄存器中的`HPET_CFG_ENABLE`标识位,设置`1`允许中断). 最后,我们只需调用`hpet_clocksource_register`函数来注册新的时钟源:

```C
if (hpet_clocksource_register())
    goto out_nohpet;
```

下面的调用已经很熟悉了函数了

```C
clocksource_register_hz(&clocksource_hpet, (u32)hpet_freq);
```

`clocksource_hpet` 是频率是`250`的`clocksource`结构 (`refined_jiffies` 时钟源频率 `2`), `hpet` and `read_hpet` 回调函数用于 `HPET`的读取原子计数器.

```C

static struct clocksource clocksource_jiffies = {
    .name       = "jiffies",
    .rating     = 1, /* lowest valid rating*/
    .read       = jiffies_read,
    .mask       = CLOCKSOURCE_MASK(32),
    .mult       = TICK_NSEC << JIFFIES_SHIFT, /* details above */
    .shift      = JIFFIES_SHIFT,
    .max_cycles = 10,
};
...
static struct clocksource refined_jiffies;
...
int register_refined_jiffies(long cycles_per_second)
{
    refined_jiffies = clocksource_jiffies;
    refined_jiffies.name = "refined-jiffies";
    refined_jiffies.rating++;
    ...
```

> /kernel/time/jiffies.c 文中提到的`refined_jiffies`时钟源的定义, 可以看到是在 `jiffies`基础上进行加1操作

```C
static struct clocksource clocksource_hpet = {
    .name       = "hpet",
    .rating     = 250,
    .read       = read_hpet,
    .mask       = HPET_MASK,
    .flags      = CLOCK_SOURCE_IS_CONTINUOUS,
    .resume     = hpet_resume_counter,
};
```

> 与原文相比较没有了`.archdata = { .vclock_mode = VCLOCK_HPET },` [\[PATCH\] x86: Remove hpet vclock support](https://lore.kernel.org/lkml/d348afdb2f6acc41dde0381396226b69a0358f8b.1391210357.git.luto@amacapital.net/) 从说明可以看到由于`HPET`缓慢的表现，关闭了通过vdso获取时间的渠道,用户态无法直接调用其提供方法获取时间。

`clocksource_hpet` 注册后, 从 `hpet_time_init()` 函数返回 [arch/x86/kernel/time.c](https://github.com/torvalds/linux/blob/master/arch/x86/kernel/time.c). 最后一步调用:

```C
setup_default_timer_irq();
```

`setup_default_timer_irq` 函数检测已经存在的`legacy`中断号，或者说是否支持[i8259](https://en.wikipedia.org/wiki/Intel_8259) , [IRQ0](https://en.wikipedia.org/wiki/Interrupt_request_%28PC_architecture%29#Master_PIC) 的设置依赖于此。

从这一刻开始， [High Precision Event Timer](https://en.wikipedia.org/wiki/High_Precision_Event_Timer) 时钟源完成了linux 内核的 `clock source` 框架的注册，可以通过`read_hpet`, 从通用内核代码中使用：

```C
static cycle_t read_hpet(struct clocksource *cs)
{
    return (cycle_t)hpet_readl(HPET_COUNTER);
}
```

这个函数只是从`Main Counter Register`中读取和返回原子计数器的值.

## ACPI PM timer

第二个时钟源[ACPI Power Management Timer](http://uefi.org/sites/default/files/resources/ACPI_5.pdf). 代码实现位于 [drivers/clocksource/acpi\_pm.c](https://github.com/torvalds/linux/blob/master/drivers/clocksource/acpi_pm.c) 文件中, `fs` [initcall](https://kernelnewbies.org/Documents/InitcallMechanism)期间从对 `init_acpi_pm_clocksource`函数调用开始.

如果我们看一下`init_acpi_pm_clocksource`函数的实现，我们将看到它是从检查`pmtmr_ioport`变量的值开始的：

```C
static int __init init_acpi_pm_clocksource(void)
{
    ...
    ...
    ...
    if (!pmtmr_ioport)
        return -ENODEV;
    ...
    ...
    ...
```

变量`pmtmr_ioport` 包含`Power Management Timer Control Register Block`的扩展地址. 通过定义在[arch/x86/kernel/acpi/boot.c](https://github.com/torvalds/linux/blob/master/arch/x86/kernel/acpi/boot.c) `acpi_parse_fadt` 函数获得. 这个函数解析 `FADT` 或者 `Fixed ACPI Description Table` [ACPI](https://en.wikipedia.org/wiki/Advanced_Configuration_and_Power_Interface)表并尝试获取`X_PM_TMR_BLK`字段的值，而该字段包含`Power Management Timer Control Register Block`的扩展地址, 以 `Generic Address Structure` 格式表示:

```C
static int __init acpi_parse_fadt(struct acpi_table_header *table)
{
#ifdef CONFIG_X86_PM_TIMER
        ...
        ...
        ...
        pmtmr_ioport = acpi_gbl_FADT.xpm_timer_block.address;
        ...
        ...
        ...
#endif
    return 0;
}
```

因此，如果Linux内核关闭`CONFIG_X86_PM_TIMER`选项或者`acpi_parse_fadt`函数出现问题，我们将无法从 `init_acpi_pm_clocksource`访问`Power Management Timer`并.换句话说，如果`pmtmr_ioport`变量不为0, 我们将检查此计数器的频率并通过调用以下命令注册时钟源:

```C
clocksource_register_hz(&clocksource_acpi_pm, PMTMR_TICKS_PER_SEC);
```

在调用`clocksource_register_hs`之后，`acpi_pm`时钟源将被注册到Linux内核的`clocksource`框架中：

```C
static struct clocksource clocksource_acpi_pm = {
    .name       = "acpi_pm",
    .rating     = 200,
    .read       = acpi_pm_read,
    .mask       = (u64)ACPI_PM_MASK,
    .flags      = CLOCK_SOURCE_IS_CONTINUOUS,
};
```

> /drivers/clocksource/acpi\_pm.c

在频率为`200`情况下`acpi_pm_read`读取`acpi_pm`时钟源提供的原子计数器。 `acpi_pm_read`函数仅执行`read_pmtmr`函数：

```C
static u64 acpi_pm_read(struct clocksource *cs)
{
    return (u64)read_pmtmr();
}
```

上面函数读取 `Power Management Timer`的值. 相关数据结构如下:

```
+-------------------------------+----------------------------------+
|                               |                                  |
|  upper eight bits of a        |      running count of the        |
| 32-bit power management timer |     power management timer       |
|                               |                                  |
+-------------------------------+----------------------------------+
31          E_TMR_VAL           24               TMR_VAL           0
```

数据存储再 `Fixed ACPI Description Table` [ACPI](https://en.wikipedia.org/wiki/Advanced_Configuration_and_Power_Interface) 中我们将它存储到`pmtmr_ioport`.因此 `read_pmtmr`的实现很简单:

```C
static inline u32 read_pmtmr(void)
{
    return inl(pmtmr_ioport) & ACPI_PM_MASK;
}

```

我们仅读取`Power Management Timer`数据的低`24`位.

下面我们开始最后一个时钟源 -`Time Stamp Counter`.

## Time Stamp Counter

本章节最后一个时钟源[Time Stamp Counter](https://en.wikipedia.org/wiki/Time_Stamp_Counter) 实现代码在 [arch/x86/kernel/tsc.c](https://github.com/torvalds/linux/blob/master/arch/x86/kernel/tsc.c) 中.我们已经看到了 [Time Stamp Counter](https://en.wikipedia.org/wiki/Time_Stamp_Counter) 初始化是从`x86_late_time_init`开始 . 这个函数调用 `tsc_init()` , 实现在 [arch/x86/kernel/tsc.c](https://github.com/torvalds/linux/blob/master/arch/x86/kernel/tsc.c) 文件中.

`tsc_init`函数开始可以看到其检查处理器是否支持`Time Stamp Counter`:

```C
void __init tsc_init(void)
{
    u64 lpj;
    int cpu;

    if (!boot_cpu_has(X86_FEATURE_TSC)) {
        setup_clear_cpu_cap(X86_FEATURE_TSC_DEADLINE_TIMER);
        return;
    }
    ...
    ...
    ...
```

`boot_cpu_has` 宏定义扩展为 宏`cpu_has`:

```C

#define boot_cpu_has(bit)  cpu_has(&boot_cpu_data, bit)

#define cpu_has(c, bit)                            \
    (__builtin_constant_p(bit) && REQUIRED_MASK_BIT_SET(bit) ? 1 :  \
     test_cpu_cap(c, bit))
```

检查`boot_cpu_data`信息(我们的例子里是 `X86_FEATURE_TSC_DEADLINE_TIMER` ) 这些数据在在linux内核初始化的时候被填充，如果处理器支持`Time Stamp Counter`, 我们可以通过`calibrate_tsc`获取`Time Stamp Counter` 的频率, 类似于从 [Model Specific Register](https://en.wikipedia.org/wiki/Model-specific_register)获取不同源的频率, 并通过[programmable interval timer](https://en.wikipedia.org/wiki/Programmable_interval_timer), 我们将初始化系统中所有处理器的频率和比例因子：

> 模型的寄存器(MSR)是x86指令集中用于调试、程序执行跟踪、计算机性能监视和切换某些CPU特性的各种控制寄存器。

```C

void __init tsc_early_init(void)
{
    if (!boot_cpu_has(X86_FEATURE_TSC))
        return;
    /* Don't change UV TSC multi-chassis synchronization */
    if (is_early_uv_system())
        return;
    if (!determine_cpu_tsc_frequencies(true))
        return;
    loops_per_jiffy = get_loops_per_jiffy();

    tsc_enable_sched_clock();
}

...
void __init tsc_init(void)

...
if (!tsc_khz) {
    /* We failed to determine frequencies earlier, try again */
    if (!determine_cpu_tsc_frequencies(false)) {
        mark_tsc_unstable("could not calculate TSC khz");
        setup_clear_cpu_cap(X86_FEATURE_TSC_DEADLINE_TIMER);
        return;
    }
    tsc_enable_sched_clock();
}

cyc2ns_init_secondary_cpus();

...

```

因为只有第一个引导程序处理器才会调用`tsc_init`。 在此之后，对所有cpu上的`TSC`是否可信和同步进行判断：

> 避免在`TSC`没有正确同步的多套接字系统上出现时间差异，从而排除`TSC`用于计时。但是，这也会阻止使用TSC作为sched clock()，这是不必要的，因为核心sched clock()实现可以很好地处理基于TSC的非同步sched时钟。

```C
- if (tsc_disabled > 0)
- return;
+ if (unsynchronized_tsc()) {
+   mark_tsc_unstable("TSCs unsynchronized");
+   return;
+ }
...
...
...
check_system_tsc_reliable();
```

> 原文中的`tsc_disabled`在 [x86/tsc: Redefine notsc to behave as tsc=unstable](https://github.com/torvalds/linux/commit/fe9af81e524e8a86bdd59c0cc0d9e2b0ccaf840f)弃用了， 原因是notsc内核参数禁止sched clock()使用TSC。但是，这个参数并不会阻止内核访问其他地方的tsc, 为了兼容性考虑使用`tsc_unstable`，

如果引导程序处理器支持`X86_FEATURE_TSC_RELIABLE`，则调用`check_system_tsc_reliable`函数，该函数设置`tsc_clocksource_reliable`。 请注意，我们经历了`tsc_init`函数，但未注册我们的时钟源。 `TSC`的实际注册发生在：

```C
static int __init init_tsc_clocksource(void)
{
    if (!boot_cpu_has(X86_FEATURE_TSC) || !tsc_khz)
        return 0;

    if (tsc_unstable)
        goto unreg;

    if (tsc_clocksource_reliable || no_tsc_watchdog)
        clocksource_tsc.flags &amp;= ~CLOCK_SOURCE_MUST_VERIFY;

    if (boot_cpu_has(X86_FEATURE_NONSTOP_TSC_S3))
        clocksource_tsc.flags |= CLOCK_SOURCE_SUSPEND_NONSTOP;

    /*
     * When TSC frequency is known (retrieved via MSR or CPUID), we skip
     * the refined calibration and directly register it as a clocksource.
     */
    if (boot_cpu_has(X86_FEATURE_TSC_KNOWN_FREQ)) {
        if (boot_cpu_has(X86_FEATURE_ART))
            art_related_clocksource = &amp;clocksource_tsc;
        clocksource_register_khz(&amp;clocksource_tsc, tsc_khz);
unreg:
        clocksource_unregister(&amp;clocksource_tsc_early);
        return 0;
    }

    schedule_delayed_work(&amp;tsc_irqwork, 0);
    return 0;
}
```

在设备初始化 `device` [initcall](https://kernelnewbies.org/Documents/InitcallMechanism). 调用期间调用此函数。 我们这样做是为了确保在 [High Precision Event Timer](https://en.wikipedia.org/wiki/High_Precision_Event_Timer) 时钟源之后注册时间戳计数器时钟源。

在这三个时钟源之后，所有这三个时钟源都将被注册到时钟源框架中，并且`Time Stamp Counter`将被选择为活动时钟，因为它在其他时钟源中具有最高的频率.

```C
static struct clocksource clocksource_tsc = {
    .name                   = "tsc",
    .rating                 = 300,
    .read                   = read_tsc,
    .mask                   = CLOCKSOURCE_MASK(64),
    .flags                  = CLOCK_SOURCE_IS_CONTINUOUS | CLOCK_SOURCE_MUST_VERIFY,
    .archdata               = { .vclock_mode = VCLOCK_TSC },
};
```

## 小结

这是本章 [chapter](https://0xax.gitbooks.io/linux-insides/content/Timers/index.html) 第六部分的结尾，它描述了Linux内核中与计时器和计时器管理相关的内容。 在上一部分中，您熟悉了`clockevents`框架。 在这一部分中，我们继续学习Linux内核中与时间管理相关的内容，并了解了[x86](https://en.wikipedia.org/wiki/X86) architecture中使用的三种不同的时钟源。 下一部分将是本章的最后一部分，我们将看到一些与用户空间有关的内容，即，如何在Linux内核中实现一些与时间有关的系统调用[system calls](https://en.wikipedia.org/wiki/System_call).

如果您有任何问题或建议，请随时在twitter [0xAX](https://twitter.com/0xAX)上ping我，给我发电子邮件[email](anotherworldofworld@gmail.com)或者只创建[问题](https://github.com/0xAX/linux-insides/issues/new)。

**请注意，英语不是我的第一语言，我很抱歉给您带来不便。如果您发现任何错误，请将PR发送至[linux-insides](https://github.com/0xAX/linux-insides)。**

## 相关链接

- [x86](https://en.wikipedia.org/wiki/X86)
- [sysfs](https://en.wikipedia.org/wiki/Sysfs)
- [Time Stamp Counter](https://en.wikipedia.org/wiki/Time_Stamp_Counter)
- [High Precision Event Timer](https://en.wikipedia.org/wiki/High_Precision_Event_Timer)
- [ACPI Power Management Timer (PDF)](http://uefi.org/sites/default/files/resources/ACPI_5.pdf)
- [frequency](https://en.wikipedia.org/wiki/Frequency).
- [dmesg](https://en.wikipedia.org/wiki/Dmesg)
- [programmable interval timer](https://en.wikipedia.org/wiki/Programmable_interval_timer)
- [IRQ](https://en.wikipedia.org/wiki/Interrupt_request_%28PC_architecture%29)
- [IA-PC HPET (High Precision Event Timers) Specification](http://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/software-developers-hpet-spec-1-0a.pdf)
- [IRQ0](https://en.wikipedia.org/wiki/Interrupt_request_%28PC_architecture%29#Master_PIC)
- [i8259](https://en.wikipedia.org/wiki/Intel_8259)
- [initcall](https://kernelnewbies.org/Documents/InitcallMechanism)
- [previous part](https://0xax.gitbooks.io/linux-insides/content/Timers/linux-timers-5.html)

