2.6 Timer and List "Go" in the kernel

Source: Internet
Author: User
Tags emit

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). jiffiesglobal 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.

List_head (new_list)

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_moduleThe 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

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.