1. There are two types of clocks under Linux:
1.1 Real Clock RTC
The "Real Time Clock", which is powered by the onboard battery, is also called the RTC or the CMOS clock, the hardware clock. When the operating system shuts down, use this to record the time, but for the running system is not the time.
1.2 System clock
"System Clock" is also called the kernel clock or software clock, is the software based on time interrupt to count, the kernel clock in the case of the system shutdown is not present, so when the operating system starts, the kernel clock is to read the RTC time to synchronize time.
2. Standard Timer
2.1 Several basic parameters of clock tick (jiffies)
2.1.1 Clock cycle frequency-crystal oscillator frequency
Timer timers the number of clock pulses generated in 1 seconds is the frequency of the clock cycle, pay attention to the timer clock cycle frequency and clock interrupt frequency distinguish, Linux with macro Clock_tick_ rate to indicate the frequency of the timer's input clock pulses (for example, my # # clock_tick_rate 1193180), which is defined in the Arm/mach-xxx/include/mach/timex.h header file.
2.1.2 Clock Interrupt (clocks tick)
We know that when the counter is reduced to a value of 0, it generates a clock interrupt on the IRQ0, which is a clock interrupt, and the initial value of the counter determines how many clock cycles are required to produce a clock interrupt, thus determining the length of time between ticks of a clock.
2.1.3 Clock Interrupt frequency (HZ)
That is, the number of clock interrupts generated by the timer in the 1-second period. When the frequency value of the clock interrupt is determined, the counter value of the timer can be determined. The Linux kernel uses the macro Hz to represent the frequency of clock interrupts, and the Hz has different defined values on different platforms. The values for platform Hz such as SPARC, MIPS, ARM, and i386 are all 100. The macro is defined on the ARM platform as follows (/arch/arm/include/asm/param.h)
2.1.4 The initial value of the counter
The initial value of the counter is defined by the macro latch in the file: Include/linux/jiffies.h
#define LATCH ((clock_tick_rate + HZ/2)/HZ)/* for divider */
2.1.5 Jiffies
In the Linux kernel, the time is measured by a global variable called jiffies, which identifies the number of ticks that have elapsed since the system was started. At the lowest level, the way the ticks are calculated depends on the specific hardware platform that is running, but the tick count usually continues during an outage.
The standard timer API has been a part of the Linux kernel for a long time (since earlier versions of the Linux kernel). Although it provides less accuracy than a high-precision timer, it is ideal for traditional driver timeouts that provide error coverage when dealing with physical devices. In many cases, these timeouts are actually never triggered, but are started and then deleted.
The simple kernel timer is implemented using the timer wheel (wheel). The idea was first introduced by Finn Arne Gangstad in 1997. It ignores the problem of managing large numbers of timers, but rather manages a reasonable number of timers-typically. (The original timer implementation only implements double chaining of timers in an out-of-date order.) Although conceptually simple, this approach is not scalable. The time wheel is a buckets collection, where each bucker represents a time block in the future when the timer expires. These buckets are defined using a logarithmic time based on 5 buckets. Using Jiffies as a time granularity, several groups are defined that represent future expiration periods, where each group is represented by a list of timers. Timer insertion occurs with a list operation with O (1) complexity, and expiration occurs within O (N) time. Timer expiration occurs in series, where timers are removed from high-granularity buckets and then are inserted into the low-granularity buckets as their expiration time falls. Now let's take a look at the APIs implemented for this timer.
2.2 Timer API
Linux provides a simple API to construct and manage timers. It contains functions (and helper functions) for creating, canceling, and managing timers.
The timer is defined by the timer_list structure, which includes all the data required to implement a timer (including list pointers and optional timer statistics configured at compile time). From the user's point of view, Timer_list contains an expiration time, a callback function (when/if the timer expires), and a user-supplied context. The user must initialize the timer and can take several methods, the simplest method being to call Setup_timer, which initializes the timer and sets the user-supplied callback function and context. Alternatively, the user can set these values (functions and data) in the timer and simply call Init_timer. Note that Init_timer is called internally by Setup_timer.
[CPP]View PlainCopy
- void Init_timer ( struct timer_list *timer);
- void Setup_timer ( struct timer_list *timer,
- void (*function) (unsigned long), unsigned Long data);
After having an initialized timer, the user now needs to set the expiration time, which is done by calling Mod_timer. Since users typically provide a future expiration time, they typically add jiffies here to offset from the current time. The user can also delete a timer by calling Del_timer (if it has not expired):
[CPP]View PlainCopy
- int Mod_timer ( struct timer_list *timer, unsigned long expires);
- void Del_timer ( struct timer_list *timer);
Finally, the user can find out if the timer is waiting (not yet issued) by calling Timer_pending (which will return 1 if it is waiting):
[CPP]View PlainCopy
- int timer_pending ( const struct timer_list *timer);
2.3 Timer Example
Let's look at the actual operation of these API functions. The following code provides a simple kernel module that demonstrates the core features of the simple timer API. In Init_module, you initialize a timer with Setup_timer and then call Mod_timer to start it. When the timer expires, the callback function my_timer_callback is called. Finally, when you delete a module, the timer is removed (via Del_timer). (Note the return check from Del_timer, which determines whether the timer is still in use.) )
[CPP]View PlainCopy
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/timer.h>
- Module_license ("GPL");
- Static struct timer_list my_timer;
- void My_timer_callback (unsigned Long data)
- {
- PRINTK ( "My_timer_callback called (%ld). \ n", jiffies);
- }
- int init_module ( void)
- {
- int ret;
- PRINTK ("Timer module installing\n");
- //my_timer.function, My_timer.data
- Setup_timer (&my_timer, my_timer_callback, 0);
- PRINTK ( "Starting timer to fire in 200ms (%ld) \ n", jiffies);
- ret = Mod_timer (&my_timer, Jiffies + msecs_to_jiffies (200));
- if (ret) PRINTK ("Error in mod_timer\n");
- return 0;
- }
- void Cleanup_module ( void)
- {
- int ret;
- ret = Del_timer (&my_timer);
- if (ret) PRINTK ("The timer is still in use...\n");
- PRINTK ("Timer module uninstalling\n");
- return;
- }
3. High accuracy timer (Hrtimer)
The high-precision timers (hrtimers) provide a high-precision timer management framework that is independent of the standard timer framework discussed earlier, because merging the two frameworks is too complex. Although the timer runs on jiffies granularity, Hrtimers runs on the nanosecond granularity.
The Hrtimer framework is implemented differently than the standard timer API. Instead of using buckets and concatenation, Hrtimer maintains a time-ordered timer data structure that inserts timers in chronological order to minimize processing at the time of activation. This data structure is a "red-black" tree that is ideal for performance-focused applications (and is generally available as a library in the kernel).
The Hrtimer framework is available as an API in the kernel, and user-space applications can also be nanosleep, itimers, and portable Operating System Interface (POSIX)-timers Interface to make Use it. The Hrtimer framework is mainline (mainlined) into the 2.6.21 kernel.
3.1 High Accuracy Timer API
The Hrtimer API is somewhat similar to traditional APIs, but some of the fundamental differences between them are that it allows for additional time control. The 1th that should be noted is that time is not represented by jiffies, but by a special data type called Ktime. This representation hides some of the details of managing time effectively on this granularity. The Hrtimer API formally confirms (formalize) the difference between absolute time and relative time, requiring the caller to specify the type.
[CPP]View PlainCopy
- UNION&NBSP;KTIME&NBSP;{&NBSP;&NBSP;
- s64 tv64;
- #if bits_per_long != 64 && !defined (config_ktime_scalar)
- struct {
- #&NBSP;IFDEF&NBSP;__BIG_ENDIAN&NBSP;&NBSP;
- s32 sec, nsec;
- # else
- s32 nsec, sec;
- #&NBSP;ENDIF&NBSP;&NBSP;
- } tv;
- #endif
- };
Similar to the traditional timer API, high-precision timers are represented by a structure-here is hrtimer. This structure defines the timer (callback function, expiration time, etc.) from the user's perspective and contains management information (where the timer exists in the red-black tree, optional statistics, etc.).
The definition process Initializes a timer first through Hrtimer_init. This call contains a timer, a clock definition, and a timer pattern (absolute or relative). The clocks used are defined in./include/linux/time.h, which represents the various clocks supported by the system (such as a real-time clock or a single 1:, which only represents the time from a starting point, such as the start of a system). After the timer is initialized, it can be started by Hrtimer_start. This call contains the mode (absolute or relative) of the expiration Time (in ktime_t) and the time value.
[CPP]View PlainCopy
- struct Hrtimer {
- struct Rb_node node;
- ktime_t _expires;
- ktime_t _softexpires;
- enum Hrtimer_restart (*function) (struct hrtimer *);
- struct hrtimer_clock_base *base;
- unsigned long state;
- #ifdef Config_timer_stats
- int start_pid;
- void *start_site;
- Char start_comm[16];
- #endif
- };
[CPP]View PlainCopy
- void Hrtimer_init ( struct Hrtimer *time, clockid_t Which_clock,
- enum Hrtimer_mode mode);
- int Hrtimer_start (struct Hrtimer *timer, ktime_t time, const
- enum Hrtimer_mode mode);
After the Hrtimer is started, it can be canceled by calling Hrtimer_cancel or Hrtimer_try_to_cancel. Each function contains a hrtimer reference to the timer that will be stopped. The difference between the two functions is that the Hrtimer_cancel function attempts to cancel the timer, but if the timer has been issued, it waits for the callback function to end, and the Hrtimer_try_to_cancel function tries to cancel the timer, but if the timer has already been issued, it will return a failure.
[CPP]View PlainCopy
- int Hrtimer_cancel (struct hrtimer *timer);
- int Hrtimer_try_to_cancel (struct hrtimer *timer);
You can check whether Hrtimer has activated its callback function by calling Hrtimer_callback_running. Note that this function is called internally by Hrtimer_try_to_cancel to return an error when the callback function of the timer is called.
[CPP]View PlainCopy
- int hrtimer_callback_running (struct hrtimer *timer);
3.2 A hrtimer example
[CPP]View PlainCopy
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/hrtimer.h>
- #include <linux/ktime.h>
- Module_license ("GPL");
- #define MS_TO_NS (x) (x * 1e6l)
- Static struct Hrtimer hr_timer;
- Enum Hrtimer_restart my_hrtimer_callback ( struct hrtimer *timer)
- {
- PRINTK ( "My_hrtimer_callback called (%ld). \ n", jiffies);
- return hrtimer_norestart;
- }
- int init_module ( void)
- {
- ktime_t Ktime;
- unsigned long Delay_in_ms = 200L;
- PRINTK ("HR Timer module installing\n");
- Ktime = Ktime_set (0, Ms_to_ns (Delay_in_ms));
- Hrtimer_init (&hr_timer, Clock_monotonic, Hrtimer_mode_rel);
- Hr_timer.function = &my_hrtimer_callback;
- PRINTK ( "Starting timer to fire in%ldms (%ld) \ n", Delay_in_ms, jiffies);
- Hrtimer_start (&hr_timer, Ktime, Hrtimer_mode_rel);
- return 0;
- }
- void Cleanup_module ( void)
- {
- int ret;
- ret = Hrtimer_cancel (&hr_timer);
- if (ret) PRINTK ("The timer is still in use...\n");
- PRINTK ("HR Timer module uninstalling\n");
- return;
- }
There's a lot of content about the Hrtimer API that is not covered here. An interesting aspect is that it can define the execution context of the callback function (for example, in the context of SOFTIRQ or HARDIIRQ). You can learn more about the Hrtimer API in the./include/linux/hrtimer.h file.
Clock mechanism of ARM Linux system