# linux-sides-Timers and time management in the Linux kernel. Part 5.
Date: 2020-01-19


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

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

## clockevents框架简介

这是[本章节](https://0xax.gitbooks.io/linux-insides/content/Timers/index.html)的第五部分，它描述了Linux内核中与计时器和时间管理相关的内容。正如您可能从本部分的标题中注意到的那样，将讨论`clockevents`框架。我们已经在本章的[第二部分](https://0xax.gitbooks.io/linux-insides/content/Timers/linux-timers-2.html)中看到了一个框架。这是`clocksource`框架。这两个框架都是Linux内核中的计时抽象。

首先，让我们回忆一下`clocksource`框架以及它的用途。 clocksource框架的主要目标是提供timeline。如[documentation](https://github.com/0xAX/linux/blob/master/Documentation/timers/timekeeping.txt)中所述：

> 例如，在Linux终端上输入命令`date`将最终读取时钟源以确定确切的时间。

Linux内核支持许多不同的时钟源。 您可以在[drivers/clocksource](https://github.com/torvalds/linux/tree/master/drivers/clocksource)中找到其中的一些。 例如，老一些的比如[Intel 8253](https://en.wikipedia.org/wiki/Intel_8253)\-[可编程间隔计时器](https://en.wikipedia.org/wiki/Programmable_interval_timer)，频率为`1193182`, 还有一个-[ACPI PM](http://uefi.org/sites/default/files/resources/ACPI_5.pdf)计时器，频率为`3579545`Hz。 除了[drivers/clocksource](https://github.com/torvalds/linux/tree/master/drivers/clocksource)目录之外，每种体系结构都可以提供自己的体系结构专用时钟源。 例如[x86](https://en.wikipedia.org/wiki/X86)架构提供了[高精度事件计时器](https://en.wikipedia.org/wiki/High_Precision_Event_Timer)，或者例如是[powerpc](https://zh.wikipedia.org/wiki/PowerPC)通过`timebase`寄存器提供对处理器计时器的访问。

每个时钟源都提供单调原子计数器。 Linux内核支持大量不同的时钟源，每个时钟源都有自己的参数，例如[frequency](https://en.wikipedia.org/wiki/Frequency)。 `clocksource`框架的主要目标是提供[API](https://en.wikipedia.org/wiki/Application_programming_interface)，以选择系统中最佳的可用时钟源，即频率最高的时钟源。 `clocksource`框架的另一个目标是由时钟源提供的以人类时间单位的表示的原子计数器。 在这个时候，纳秒是Linux内核中给定时钟源的时间值单位的首选。

`clocksource`框架被定义在[include/linux/clocksource.h](https://github.com/torvalds/linux/blob/master/include/linux/clocksource.h)头文件中的`clocksource`结构中，其中包含时钟源的`name`，系统中某些时钟源的额定值(频率较高的时钟源在系统中具有最大的额定值)，系统中所有已注册时钟源的`list`，`enable`和`disable`字段来启用和禁用时钟源，指向`read`函数(必须返回时钟源的原子计数器)的指针等等。

另外，`clocksource`结构提供了两个字段：`mult`和`shift`，这是由某个时钟源提供给人类计时单位的原子计数器转换所必需的，即[nanoseconds](https://en.wikipedia.org/wiki/Nanosecond).通过以下公式进行转换：

```C
ns ~= (clocksource * mult) &gt;&gt; shift
```

我们已经知道，除了`clocksource`结构之外，`clocksource`框架还提供了一个API，用于注册具有不同频率比例因子的时钟源：

```C
static inline int clocksource_register_hz(struct clocksource *cs, u32 hz)
static inline int clocksource_register_khz(struct clocksource *cs, u32 khz)
```

时钟源注销:

```C
int clocksource_unregister(struct clocksource *cs)
```

...

除了`clocksource`框架之外，Linux内核还提供了`clockevents`框架。 如[文档](https://github.com/0xAX/linux/blob/master/Documentation/timers/timekeeping.txt)中所述：

> 时钟事件与时钟源在概念上相反

主要目标是管理时钟事件设备，或者换句话说，即管理允许注册事件的设备，换句话说，就是要进行以下操作的设备：[中断](https://en.wikipedia.org/wiki/Interrupt)将在将在将来的指定时间发生。

现在，我们对Linux内核中的`clockevents`框架有了一些了解，现在是时候来看一下[API](https://en.wikipedia.org/wiki/Application_programming_interface).

## `clockevents`框架相关API

描述时钟事件设备的主要结构是`clock_event_device`结构。 此结构在[include/linux/clockchips.h](https://github.com/torvalds/linux/blob/master/include/linux/clockchips.h)头文件中定义，并且包含大量字段。 除了`clocksource`结构之外，它还具有`name`字段，其中包含时钟事件设备的可读名称，例如[local APIC](https://en.wikipedia.org/wiki/Advanced_Programmable_Interrupt_Controller)计时器：

```C
static struct clock_event_device lapic_clockevent = {
    .name                   = "lapic",
    ...
    ...
    ...
}
```

某个时钟事件设备的`event_handler`，`set_next_event`，`next_event`函数的地址是[中断处理程序](https://en.wikipedia.org/wiki/Interrupt_handler)，下一个事件的设置程序和本地分别存储下一个事件。 `clock_event_device`结构的另一个字段是-`features`字段。 它的价值可能来自以下通用功能：

```C
#define CLOCK_EVT_FEAT_PERIODIC 0x000001
#define CLOCK_EVT_FEAT_ONESHOT  0x000002
```

其中`CLOCK_EVT_FEAT_PERIODIC`表示可以被编程为定期生成事件的设备。 `CLOCK_EVT_FEAT_ONESHOT`代表只能产生一次事件的设备。 除了这两个功能，还有特定于体系结构的功能。 例如，[x86\_64](https://en.wikipedia.org/wiki/X86-64)支持两个附加功能：

```C
#define CLOCK_EVT_FEAT_C3STOP 0x000008
```

The first `CLOCK_EVT_FEAT_C3STOP` means that a clock event device will be stopped in the [C3](https://en.wikipedia.org/wiki/Advanced_Configuration_and_Power_Interface#Device_states) state. Additionally the `clock_event_device` structure has `mult` and `shift` fields as well as `clocksource` structure. The `clocksource` structure also contains other fields, but we will consider it later.

第一个`CLOCK_EVT_FEAT_C3STOP`表示时钟事件设备将在[C3](https://en.wikipedia.org/wiki/Advanced_Configuration_and_Power_Interface#Device_states)状态下停止。 另外，clock\_event\_device结构具有`mult`和`shift`字段以及`clocksource`结构。 `clocksource`结构还包含其他字段，但我们稍后会考虑。

在考虑了`clock_event_device`结构的一部分之后，我们来看看`clockevents`框架的API。 要使用时钟事件设备，首先我们需要初始化`clock_event_device`结构并注册一个时钟事件设备。 `clockevents`框架提供以下`API`来注册时钟事件设备

```C
void clockevents_register_device(struct clock_event_device *dev)
{
   ...
   ...
   ...
}
```

在[kernel/time/clockevents.c](https://github.com/torvalds/linux/blob/master/kernel/time/clockevents.c)源代码文件中定义的函数，正如我们所看到的， `clockevents_register_device`函数仅采用一个参数：

\*代表时钟事件设备的`clock_event_device`结构的地址。

因此，要注册一个时钟事件设备，首先我们需要使用某个时钟事件设备的参数来初始化`clock_event_device`结构。 让我们看一下Linux内核源代码中的一个随机时钟事件设备。 我们可以在[drivers/clocksource](https://github.com/torvalds/linux/tree/master/drivers/clocksource)目录中找到一个，或尝试查看特定于体系结构的时钟事件设备。 让我们举个例子-[at91sam926x 的定期间隔计时器(PIT)](http://www.keil.com/dd/docs/datashts/atmel/at91sam9260_dc.pdf)

> 原书给的链接已失效,这里修改链接，文档中19章节有关于PIT描述 P155

您可以在[drivers/clocksource](https://github.com/torvalds/linux/tree/master/drivers/clocksource/timer-atmel-pit.c)中找到其实现。

首先，让我们看一下`clock_event_device`结构的初始化。 这发生在`at91sam926x_pit_common_init`函数中：

```C
struct pit_data {
    ...
    ...
    struct clock_event_device       clkevt;
    ...
    ...
};

static int __init at91sam926x_pit_dt_init(struct device_node *node)
{
    ...
    ...
    ...
    struct pit_data *data;
    data->clksrc.mask = CLOCKSOURCE_MASK(bits);
    data->clksrc.name = "pit";
    data->clksrc.rating = 175;
    data->clksrc.read = read_pit_clk;
    data->clksrc.flags = CLOCK_SOURCE_IS_CONTINUOUS;

    data->clkevt.name = "pit";
    data->clkevt.features = CLOCK_EVT_FEAT_PERIODIC;
    data->clkevt.shift = 32;
    data->clkevt.mult = div_sc(pit_rate, NSEC_PER_SEC, data->clkevt.shift);
    data->clkevt.rating = 100;
    data->clkevt.cpumask = cpumask_of(0);

    data->clkevt.set_state_shutdown = pit_clkevt_shutdown;
    data->clkevt.set_state_periodic = pit_clkevt_set_periodic;
    data->clkevt.resume = at91sam926x_pit_resume;
    data->clkevt.suspend = at91sam926x_pit_suspend;

    ...
}
```

> 下面根据当前版本(v5.5-rc6)进行调整

在这里我们可以看到`at91sam926x_pit_dt_init`本地变量`pit_data`结构的指针，该结构包含`clock_event_device`结构，该结构将包含`at91sam926x`的时钟事件相关信息[定期间隔计时器](https://en.wikipedia.org/wiki/Programmable_interval_timer)。首先，我们填写计时器设备的`name`及`features`。在我们的情况下，我们处理周期性的计时器，众所周知，可以将其编程为定期生成事件。

接下来的两个字段`shift`和`mult`是我们熟悉的。它们将用于将计时器的计数器转换为纳秒。此后，我们将计时器的`rating`设置为`100`。这意味着如果系统中不存在具有更高额定值的计时器，则该计时器将用于计时。下一个字段`cpumask`指示设备将在系统中的哪些处理器上运行。在我们的情况下，该设备将适用于第一个处理器。在[include/linux/cpumask.h](https://github.com/torvalds/linux/tree/master/include/linux/cpumask.h)头文件中定义的`cpumask_of`宏，并扩展为调用的：

```C
#define cpumask_of(cpu) (get_cpu_mask(cpu))
```

其中`get_cpu_mask`返回仅包含给定`cpu`编号的`cpumask`。有关`cpumasks`概念的更多信息，您可以在[Linux内核中的CPU掩码](https://0xax.gitbooks.io/linux-insides/content/Concepts/linux-cpu-2.html)部分中阅读。 在代码的最后四行中，我们为时钟事件设备挂起/恢复，设备关闭和时钟事件设备状态的更新设置了回调。

完成对`at91sam926x`定期计时器的初始化之后，我们可以通过调用以下函数进行注册：

```C
clockevents_register_device(&data->clkevt);
```

现在我们可以考虑实现`clockevents_register_device`函数。 正如我上面已经写过的，此函数在[kernel/time/clockevents.c](https://github.com/torvalds/linux/blob/master/kernel/time/clockevents.c)源代码文件中定义，并且 从初始事件设备状态的初始化开始：

```C
clockevent_set_state(dev, CLOCK_EVT_STATE_DETACHED);
```

实际上，事件设备可能处于以下状态之一:

```C
enum clock_event_state {
    CLOCK_EVT_STATE_DETACHED,
    CLOCK_EVT_STATE_SHUTDOWN,
    CLOCK_EVT_STATE_PERIODIC,
    CLOCK_EVT_STATE_ONESHOT,
    CLOCK_EVT_STATE_ONESHOT_STOPPED,
};
```

其中：

- `CLOCK_EVT_STATE_DETACHED`\-`clockevents`框架不使用时钟事件设备。 实际上，它是所有时钟事件设备的初始状态。
- `CLOCK_EVT_STATE_SHUTDOWN`\- 时钟事件设备已关闭电源；
- `CLOCK_EVT_STATE_PERIODIC`\- 时钟事件设备可以被编程为周期性地产生事件；
- `CLOCK_EVT_STATE_ONESHOT`\- 时钟事件设备可以编程为仅生成一次事件；
- `CLOCK_EVT_STATE_ONESHOT_STOPPED` - 时钟事件设备被编程为仅生成一次事件，现在暂时停止。

函数 `clock_event_set_state` 实现十分简单:

```C
static inline void clockevent_set_state(struct clock_event_device *dev, 
enum clock_event_state state)
{
    dev->state_use_accessors = state;
}
```

如我们所见，它只是使用给定值(在我们的情况下为`CLOCK_EVT_STATE_DETACHED`)填充给定`clock_event_device`结构的`state_use_accessors`字段。实际上，所有时钟事件设备在注册期间都具有此初始状态。 `clock_event_device`结构的`state_use_accessors`字段提供时钟事件设备的`当前`状态。

在设置了给定的`clock_event_device`结构的初始状态之后，我们检查给定的时钟事件设备的`cpumask`不为零：

```C
if (!dev->cpumask) {
    WARN_ON(num_possible_cpus() > 1);
    dev->cpumask = cpumask_of(smp_processor_id());
}
```

> 在注册函数中 clockevents\_register\_device

记住，我们已经将`at91sam926x`定期定时器的`cpumask`设置为第一个处理器。 如果`cpumask`字段为0，则我们检查系统中可能的处理器数量，并打印警告消息（如果少于）。 另外，我们将给定时钟事件设备的`cpumask`设置为当前处理器。 如果您对`smp_processor_id`宏的实现方式感兴趣，则可以在[第四部分](https://0xax.gitbooks.io/linux-insides/content/Initialization/linux-initialization-4.html)。

进行此检查后，我们通过调用以下宏来锁定时钟事件设备注册的实际代码：

```C
raw_spin_lock_irqsave(&clockevents_lock, flags);
...
...
...
raw_spin_unlock_irqrestore(&clockevents_lock, flags);
```

> 在注册函数中 clockevents\_register\_device

另外，`raw_spin_lock_irqsave`和`raw_spin_unlock_irqrestore`宏禁用本地中断，但是其他处理器上的中断仍然可能发生。 如果我们将新的时钟事件设备添加到时钟事件设备列表中并且其他时钟事件设备发生中断，则需要这样做以防止潜在的[死锁](https://en.wikipedia.org/wiki/Deadlock)。

我们可以在`raw_spin_lock_irqsave`和`raw_spin_unlock_irqrestore`宏之间看到以下时钟事件设备注册代码：

```C
list_add(&dev->list, &clockevent_devices);
tick_check_new_device(dev);
clockevents_notify_released();
```

首先，我们将给定的时钟事件设备添加到由`clockevent_devices`表示的时钟事件设备列表中：

```C
static LIST_HEAD(clockevent_devices);
```

在下一步，我们调用在[kernel/time/tick-common.c](https://github.com/torvalds/linux/blob/master/kernel/time/tick-common.c)源代码文件，并检查是否应使用新的已注册时钟事件设备。`tick_check_new_device`函数检查给定的`clock_event_device`获取由`tick_device`结构表示的当前注册的滴答设备，并比较它们的等级和功能。 实际上，首选`CLOCK_EVT_STATE_ONESHOT`:

```C
static bool tick_check_preferred(struct clock_event_device *curdev,
         struct clock_event_device *newdev)
{
    if (!(newdev->features & CLOCK_EVT_FEAT_ONESHOT)) {
        if (curdev && (curdev->features & CLOCK_EVT_FEAT_ONESHOT))
            return false;
    if (tick_oneshot_mode_active())
        return false;
}

    return !curdev ||
       newdev->rating > curdev->rating ||
       !cpumask_equal(curdev->cpumask, newdev->cpumask);
}
```

> /kernel/time/tick-common.c clockevents\_register\_device ->tick\_check\_new\_device

如果新注册的时钟事件设备比旧的`tick`设备更受青睐，我们将交换旧的和新的注册设备并安装新设备：

```C
clockevents_exchange_device(curdev, newdev);
tick_setup_device(td, newdev, cpu, cpumask_of(cpu));
```

> /kernel/time/tick-common.c clockevents\_register\_device ->tick\_check\_new\_device

释放`clockevents_exchange_device`函数，或者从`clockevent_devices`列表中删除旧的时钟事件设备。 下一个功能-`tick_setup_device`，我们可能会从其名称中了解到，它会设置新的tick设备。 这个函数检查新注册的时钟事件设备的模式，并调用`tick_setup_periodic`函数或`tick_setup_oneshot`取决于时钟设备模式：

```C
if (td->mode == TICKDEV_MODE_PERIODIC)
    tick_setup_periodic(newdev, 0);
else
    tick_setup_oneshot(newdev, handler, next_event);
```

> /kernel/time/tick-common.c clockevents\_register\_device ->tick\_check\_new\_device ->tick\_setup\_device

这两个函数都调用`clockevents_switch_state`以更改时钟事件设备的状态，并调用`clockevents_program_event`函数根据最大和最小当前时间差与下一个事件的时间之间的差值来设置时钟事件设备的下一个事件。 `tick_setup_periodic`

```C
clockevents_switch_state(dev, CLOCK_EVT_STATE_PERIODIC);
clockevents_program_event(dev, next, false))
```

> /kernel/time/tick-common.c clockevents\_register\_device ->tick\_check\_new\_device ->tick\_setup\_device ->tick\_setup\_periodic

`tick_setup_oneshot_periodic`:

```C
clockevents_switch_state(newdev, CLOCK_EVT_STATE_ONESHOT);
clockevents_program_event(newdev, next_event, true);
```

> 函数调用链同上

`clockevents_switch_state`函数检查时钟事件设备是否未处于给定状态，并从同一源代码文件调用`__clockevents_switch_state`函数：

```C
if (clockevent_get_state(dev) != state) {
    if (__clockevents_switch_state(dev, state))
        return;
```

> /kernel/time/clockevents.c clockevents\_register\_device ->tick\_check\_new\_device ->tick\_setup\_device ->tick\_setup\_periodic ->clockevents\_switch\_state

`__clockevents_switch_state`函数仅根据给定的状态来调用特定的回调：

```C
static int __clockevents_switch_state(struct clock_event_device *dev,
                                   enum clock_event_state state)
{
    if (dev->features & CLOCK_EVT_FEAT_DUMMY)
        return 0;

    switch (state) {
    case CLOCK_EVT_STATE_DETACHED:
    case CLOCK_EVT_STATE_SHUTDOWN:
        if (dev->set_state_shutdown)
            return dev->set_state_shutdown(dev);
        return 0;

    case CLOCK_EVT_STATE_PERIODIC:
        if (!(dev->features & CLOCK_EVT_FEAT_PERIODIC))
            return -ENOSYS;
        if (dev->set_state_periodic)
            return dev->set_state_periodic(dev);
        return 0;
    ...
    ...
    ...
```

> /kernel/time/clockevents.c clockevents\_register\_device ->tick\_check\_new\_device ->tick\_setup\_device ->tick\_setup\_periodic ->clockevents\_switch\_state ->\_\_clockevents\_switch\_state

在我们的`at91sam926x`定期计时器的情况下，状态为`CLOCK_EVT_FEAT_PERIODIC`：

```C
data->clkevt.features = CLOCK_EVT_FEAT_PERIODIC;
data->clkevt.set_state_periodic = pit_clkevt_set_periodic;
```

因此，对于`pit_clkevt_set_periodic`回调将被调用。 如果我们阅读 [at91sam926x 的定期间隔计时器(PIT)](http://www.keil.com/dd/docs/datashts/atmel/at91sam9260_dc.pdf) 我们将看到存在`定期间隔计时器模式寄存器`(Periodic Interval Timer Mode Register)， 使我们能够控制定期间隔计时器。

看起来想这样 :

```C
31                                                   25        24
+---------------------------------------------------------------+
|                                          |  PITIEN  |  PITEN  |
+---------------------------------------------------------------+
23                            19                               16
+---------------------------------------------------------------+
|                             |               PIV               |
+---------------------------------------------------------------+
15                                                              8
+---------------------------------------------------------------+
|                            PIV                                |
+---------------------------------------------------------------+
7                                                               0
+---------------------------------------------------------------+
|                            PIV                                |
+---------------------------------------------------------------+
```

其中`PIV`或`定期间隔值`\-定义了与定期间隔计时器的主要`20位`计数器相比的值。 如果该位为1，则启用`PITEN`或`周期间隔定时器`; 如果该位为1，则启用`PITIEN`或`周期间隔定时器`中断。 因此，要设置周期模式，我们需要在`周期间隔定时器模式寄存器`中设置`24`，`25`位。 我们在`pit_clkevt_set_periodic`函数中执行此操作：

```C
static int pit_clkevt_set_periodic(struct clock_event_device *dev)
{
        struct pit_data *data = clkevt_to_pit_data(dev);
        ...
        ...
        ...
        pit_write(data->base, AT91_PIT_MR,
                  (data->cycle - 1) | AT91_PIT_PITEN | AT91_PIT_PITIEN);

        return 0;
}
```

`AT91_PT_MR`, `AT91_PT_PITEN` `AT91_PIT_PITIEN` 定义如下：

```C
#define AT91_PIT_MR             0x00
#define AT91_PIT_PITIEN       BIT(25)
#define AT91_PIT_PITEN        BIT(24)
```

新时钟事件设备的设置完成后，我们可以返回`clockevents_register_device`函数。 `clockevents_register_device`函数中的最后一个函数是：

```C
clockevents_notify_released();
```

该函数检查包含释放的时钟事件设备的`clockevents_released`列表（请记住，它们可能在调用`clockevents_exchange_device`函数之后发生）。 如果此列表不为空，则从`clock_events_released`列表中浏览时钟事件设备，然后从 `clockevent_devices`列表中将其删除：

```C
static void clockevents_notify_released(void)
{
    struct clock_event_device *dev;

    while (!list_empty(&clockevents_released)) {
        dev = list_entry(clockevents_released.next,
            struct clock_event_device, list);
        list_del(&dev->list);
        list_add(&dev->list, &clockevent_devices);
        tick_check_new_device(dev);
    }
}
```

就这样。从这一刻起，我们已经注册了新的时钟事件设备。因此，`clockevents`框架的用法很简单明了。架构在时钟事件核心中注册了他们的时钟事件设备。 clockevents核心的用户可以获取时钟事件设备供其使用。 `clockevents`框架为各种与时钟相关的管理事件提供了通知机制，例如已注册或未注册的时钟事件设备，支持[CPU hotplug](https://www.kernel.org/doc/Documentation/cpu-hotplug.txt)的系统中的处理器脱机等。

我们仅看到`clockevents_register_device`函数的实现。但通常，时钟事件层[API](https://en.wikipedia.org/wiki/Application_programming_interface)很小。除了用于时钟事件设备注册的API外，`clockevents`框架还提供了安排下一个事件中断，时钟事件设备通知服务的功能，并支持时钟事件设备的挂起和恢复。

If you want to know more about `clockevents` API you can start to research following source code and header files: [kernel/time/tick-common.c](https://github.com/torvalds/linux/blob/16f73eb02d7e1765ccab3d2018e0bd98eb93d973/kernel/time/tick-common.c), [kernel/time/clockevents.c](https://github.com/torvalds/linux/blob/16f73eb02d7e1765ccab3d2018e0bd98eb93d973/kernel/time/clockevents.c) and [include/linux/clockchips.h](https://github.com/torvalds/linux/blob/16f73eb02d7e1765ccab3d2018e0bd98eb93d973/include/linux/clockchips.h).

如果您想进一步了解`clockevents` API，可以开始研究以下源代码和头文件：[kernel/time/tick-common.c](https://github.com/torvalds/linux/blob/master/kernel/time/tick-common.c)、[kernel/time/clockevents.c](https://github.com/torvalds/linux/blob/master/kernel/time/clockevents.c)和[include/linux/clockchips.h](https://github.com/torvalds/linux/blob/master/include/linux/clockchips.h)。

就这样。

## 小结

这是本章[chapter](https://0xax.gitbooks.io/linux-insides/content/Timers/index.html)第五部分的结尾，该部分描述了Linux内核中与计时器和计时器管理相关的内容。 在上一部分中，我们熟悉了`计时器`的概念。 在这一部分中，我们继续学习Linux内核中与时间管理相关的内容，并了解了另一种框架-`clockevents`。

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

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

## 相关链接

- [timekeeping documentation](https://github.com/0xAX/linux/blob/0a07b238e5f488b459b6113a62e06b6aab017f71/Documentation/timers/timekeeping.txt)
- [Intel 8253](https://en.wikipedia.org/wiki/Intel_8253)
- [programmable interval timer](https://en.wikipedia.org/wiki/Programmable_interval_timer)
- [ACPI pdf](http://uefi.org/sites/default/files/resources/ACPI_5.pdf)
- [x86](https://en.wikipedia.org/wiki/X86)
- [High Precision Event Timer](https://en.wikipedia.org/wiki/High_Precision_Event_Timer)
- [powerpc](https://en.wikipedia.org/wiki/PowerPC)
- [frequency](https://en.wikipedia.org/wiki/Frequency)
- [API](https://en.wikipedia.org/wiki/Application_programming_interface)
- [nanoseconds](https://en.wikipedia.org/wiki/Nanosecond)
- [interrupt](https://en.wikipedia.org/wiki/Interrupt)
- [interrupt handler](https://en.wikipedia.org/wiki/Interrupt_handler)
- [local APIC](https://en.wikipedia.org/wiki/Advanced_Programmable_Interrupt_Controller)
- [C3 state](https://en.wikipedia.org/wiki/Advanced_Configuration_and_Power_Interface#Device_states)
- [Periodic Interval Timer (PIT) for at91sam926x](http://www.atmel.com/Images/doc6062.pdf)
- [CPU masks in the Linux kernel](https://0xax.gitbooks.io/linux-insides/content/Concepts/linux-cpu-2.html)
- [deadlock](https://en.wikipedia.org/wiki/Deadlock)
- [CPU hotplug](https://www.kernel.org/doc/Documentation/cpu-hotplug.txt)
- [previous part](https://0xax.gitbooks.io/linux-insides/content/Timers/linux-timers-3.html)

