Transferred from: http://www.cnblogs.com/hoys/archive/2011/11/14/2248586.html
Timers are a necessary part of all operating systems and you will find multiple timer mechanisms. We'll start with a brief introduction to some of the Linux timer patterns, and then delve deeper into how they work.
The origin of the (Linux) time
In the Linux kernel, time is measured by a jiffies
global variable called, 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 tick rate (the jiffies
least significant bit) can be configured, but in the most recent 2.6 cores for x86, a tick equals 4ms (250Hz). jiffies
global variables are widely used in the kernel for several purposes, one of which is to provide the current absolute time for calculating the timeout for a timer (an example will be shown later).
Back to top of page
Kernel timers
There are several different timer patterns in the recent 2.6 kernel, the simplest and least accurate (but applicable to most instances) mode is the timer API. This API allows you to construct timers that run in the jiffies
domain (minimum 4ms timeout). There is also a high-precision timer APIthat allows you to construct timers that run in the time defined in nanoseconds. Depending on the speed at which your processor and processor are running, your mileage (mileage) may vary, but this API does provide a way to jiffies
schedule timeouts at tick intervals.
Standard Timers
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. Used 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.
Timer API
Linux provides a simple API to construct and manage timers. It contains functions (and helper functions) for creating, canceling, and managing timers.
A timer is timer_list
defined by a structure that 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, it 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 called 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 invoke them init_timer
. Note that it is init_timer
called by the setup_timer
internal.
Init_timer 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 are usually added here jiffies
to offset from the current time. The user can also del_timer
delete a timer by calling it (if it has not expired):
Mod_timer Del_timer (struct timer_list *timer); |
Finally, the user can timer_pending
1
find out whether the timer is waiting (not yet emitted) by calling (if waiting, returning):
timer_pending (const struct timer_list *timer); |
Timer example
Let's look at the actual operation of these API functions. Listing 1 provides a simple kernel module that demonstrates the core features of the simple timer API. In init_module
, you use setup_timer
initialize a timer and then call mod_timer
to start it. When the timer expires, the callback function is called my_timer_callback
. Finally, when you delete a module, the timer is removed (passed del_timer
) to occur. (Note del_timer
The return check from, which determines whether the timer is still in use.) )
Listing 1. Explore the simple timer API
# 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_callb Ack 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;} |
You can learn more about the timer API in./include/linux/timer.h. Although the simple timer API is simple and effective, it does not provide the accuracy required for real-time applications. To do this, let's look at the recently added features of Linux, which are used to support more accurate timers.
High accuracy Timers
The high-precision timers ( hrtimers) provide a high-precision timer management framework that is independent of the timer framework discussed earlier because merging the two frameworks is too complex. Although the timer runs on a jiffies
granular level, the hrtimers runs on the nanosecond granularity.
The Hrtimer framework is implemented differently than the traditional 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 nanosleep
itimers
use it through, and portable Operating System Interface (POSIX)-timers Interface. The Hrtimer framework is mainline (mainlined) into the 2.6.21 kernel.
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 jiffies
expressed, but is represented by ktime
a special data type called. 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.
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 begins by hrtimer_init
initializing a timer. This call includes a timer, a clock definition, and a timer pattern (one-shot or restart). 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 hrtimer_start
started. This call contains the pattern of the expiration time (in ktime_t
) and the time value (absolute or relative).
Hrtimer_init 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 attempts to cancel the timer, but if the timer has been issued, it will return a failure.
Hrtimer_cancel Hrtimer_try_to_cancel (struct Hrtimer *timer); |
You can hrtimer_callback_running
check to see if Hrtimer has activated its callback function by calling. Note that this function hrtimer_try_to_cancel
is called internally so that an error is returned when the callback function of the timer is called.
hrtimer_callback_running (struct Hrtimer *timer); |
Ktime API
This article does not discuss the Ktime API, which provides a rich set of functions to manage time with high precision. You can view the Ktime API in./linux/include/ktime.h.
A hrtimer example
The Hrtimer API is very simple to use, as shown in Listing 2. In init_module
, first define the relative time for the timeout (in this case, 200ms). Then, call hrtimer_init
to initialize your Hrtimer (using single 1:) and set the callback function. Finally, start the timer with the value you created earlier ktime
. When the timer is emitted, the function is called, my_hrtimer_callback
which returns to HRTIMER_NORESTART
prevent the timer from restarting automatically. In the cleanup_module
function, the timer is canceled by calling hrtimer_cancel
.
Listing 2. Explore the Hrtimer API
#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) staticstruct HrtimerHr_timer;enum hrtimer_restart my_hrtimer_callback (struct Hrtimer *timer) {PRINTK ("my_hrtimer_callback called (%ld). \ n ", jiffies); ReturnHrtimer_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.
Back to top of page
Kernel list
As mentioned earlier in this article, the list is a useful structure, and the kernel provides an effective generic usage implementation. Additionally, you will find the list below the APIs we discussed earlier. Understanding this doubly linked list API will help you develop with this efficient data structure, and you will find that the code is superfluous in this kernel that exploits the list. Now let's take a quick look at this kernel list API.
This API provides a list_head
structure for representing list headers (anchor points) and in-structure (in-structure) list pointers. Let's examine a sample structure that includes a list function (see Listing 3). Note that Listing 3 adds a list_head
structure that is used for object linkage. Note that you can add this structure anywhere in your structure list_head
-through some GCC ( list_entry
and container_of
, as defined in./include/kernel/kernel.h)-You can cancel a reference from a list pointer to a Super object.
Listing 3. Sample structure with list references
struct My_data_structure {int value; struct list_head list;}; |
As with other list implementations, a list header is required to act as the anchor point for the list. This is usually LIST_HEAD
done through a macro, which provides the declaration and initialization of the list. This macro creates a structure list_head
object on which you can add some other objects.
You can also LIST_HEAD_INIT
create a list header manually by using a macro (for example, your list header is in another structure).
After the master initialization is complete, you can manipulate the list by using list_add
and list_del
waiting for functions. Below, we'll jump to the sample code to better explain how this API is used.
List API Examples
Listing 4 provides a simple kernel module to explore several lists of AIO functions (./include/linux/list.h contains more functions). This example creates two lists, init_module
populates them with functions, and then uses cleanup_module
functions to manipulate the two lists.
At first, you created your data structure ( my_data_struct
), which contains some data and two list headers. This example shows that you can insert an object into multiple lists at the same time. Then, you create two list headers ( my_full_list
and my_odd_list
).
In the init_module
function, you create 10 data objects and use list_add
functions to load them into the list (all objects loaded into my_full_list
, all the odd value objects loaded into my_odd_list
). One thing to note here: list_add
accept two parameters, a list reference in the object that will be used, and the other is the list anchor point. This example shows the ability to insert a data object into multiple lists by using the kernel's internal mechanism to identify the Super object that contains the list reference.
cleanup_module
The function provides several other features of the API, one of which is the list_for_each
macro, which simplifies the iteration of the list. For this macro, you provide a reference to the current object ( pos
) and a list that will be iterated. For each iteration, you receive a list_head
reference that list_entry
receives the reference to identify the container object (your data structure). Specifies the list variables within your structure and structure, which are used to dereference internally and return containers.
To emit a list of singular values (odd list), use another list_for_each_entry
iteration macro named. This macro is simpler because it automatically provides the data structure and eliminates the need to execute a list_entry
function.
Finally, use list_for_each_safe
to iterate the list so that the allocated element is freed. This macro iterates through the list, but prevents the deletion of the list entry (the Delete list entry is part of the iteration action). You use list_entry
to get your data object (so that it is freed back to the kernel pool), and then use list_del
to release the entries in the list.
Listing 4. Explore List API
#include <linux/kernel.h> #include <linux/module.h> #include <linux/list.h>module_license ("GPL"); struct My_data_struct {int value; structList_headFull_list; structList_headOdd_list;};List_head(my_full_list);List_head(my_odd_list); int init_module (void) {int count; struct My_data_struct *obj; for (count = 1; count < one; count++) {obj = (struct my_data_struct *) kmalloc (sizeof (struct my_data_s truct), Gfp_kernel); Obj->value = count;List_add(&obj->full_list, &my_full_list); if (Obj->value & 0x1) {List_add(&obj->odd_list, &my_odd_list); }} return 0;} void Cleanup_module (void) {structList_head*pos, *q; struct My_data_struct *my_obj; PRINTK ("Emit full list\n");List_for_each(POS, &my_full_list) {my_obj =List_entry(POS, struct my_data_struct, full_list); PRINTK ("%d\n", My_obj->value); } printk ("Emit odd list\n");List_for_each_entry(My_obj, &my_odd_list, Odd_list) {PRINTK ("%d\n", My_obj->value); } printk ("Cleaning up\n");List_for_each_safe(POS, Q, &my_full_list) {struct my_data_struct *tmp; TMP =List_entry(POS, struct my_data_struct, full_list);List_del(POS); Kfree (TMP); } return; |
There are many other functions that are used to include the end of the list instead of adding data () to the header, list_add_tail
the connection list () list_splice
, the contents of the test List ( list_empty
), and so on. See resources for a detailed understanding of kernel list functions.
Back to top of page
Conclusion
This article explores several APIs that demonstrate the ability to isolate functionality when necessary (timer API and high-precision Hrtimer API) and the ability to write common code for Code reuse (list API). Traditional timers provide an effective mechanism for typical driver timeouts, while Hrtimer provides a higher level of service quality for more precise timer functions. The list API provides a very versatile, yet feature-rich, and efficient interface. When you write kernel code, you will encounter one or more of these APIs, so they certainly deserve further research.
2.6 Timer and List "Go" in the kernel