2.6 timer and list in Kernel

Source: Internet
Author: User

A timer is a necessary part of all operating systems and you will find multiple timer mechanisms. We will first briefly introduce some Linux timer modes, and then study their running Modes in depth.

(Linux) Origin of time

In the Linux kerneljiffiesThe global variable that identifies the number of tick answers that have elapsed since the system was started. At the lowest level, the method for calculating the tick answer count depends on the specific hardware platform that is running. However, the tick answer count usually continues during an interruption. Tick answer rate (jiffiesCan be configured, but in the recent 2.6 kernel for x86, a single tick is equal to 4 ms (250Hz ).jiffiesGlobal variables are widely used in the kernel for several purposes. One of them is the current absolute time used to calculate the timeout value of a timer (an example will be shown later ).

Back to Top

Kernel Timer

The latest 2.6 kernel has several different timer modes. The simplest and most inaccurate (but applicable to most instances) mode is the timer API. This API can be constructed injiffiesThe timer that runs in the domain (minimum 4 Ms timeout. There is anotherHigh-precision timer APIWhich allows you to construct a timer that runs in the time defined by the nanosecond. Depending on the speed at which your processor and processor run, your mileage (mileage) may be different, but this API does provide a wayjiffiesThe scheduling times out at the tick interval.

Standard Timer

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 lower accuracy than precision timers, It is ideal for traditional driver timeouts that provide error coverage when handling physical devices. In many cases, these timeouts are not actually triggered, but started and deleted.

Simple kernel timer usageTimer wheel (timer wheel). This idea was first introduced by Finn Arne gangstad in 1997. It ignores the issue of managing a large number of timers, but manages a reasonable number of timers-typical cases. (The original timer implementation only implements a dual link to the timer in the expiration order. Although it is relatively simple in concept, this method cannot be scaled .) A time wheel is a collection of buckets. Each bucket indicates a time block in which the timer will expire in the future. These buckets are defined based on the logarithm time of five buckets. UsejiffiesAs a time granularity, several groups are defined, which represent future expiration periods (each group is represented by a timer column ). The timer inserts a list operation with O (1) complexity, and the expiration occurs in O (N. Timer expiration occurs in series, where the timer is deleted from the high-granularity buckets and then inserted into the low-granularity buckets as their expiration time drops. Now let's take a look at the API implemented for this timer.

Timer
API

Linux provides a simple API to construct and manage timers. It contains functions (and helper functions) used to create, cancel, and manage timers.

Timer passedtimer_listStructure definition, which includes all the data required to implement a timer (including list pointers and optional timer statistics configured during compilation ). From the user's perspective,timer_listContains an expiration time, a callback function (when/If the timer expires), and a context provided by the user. You must initialize the timer. You can use several methods. The simplest method is to callsetup_timerThis function initializes the timer and sets the user-provided callback function and context. Alternatively, you can set these values (functions and data) in the timer and simply callinit_timer. Note,init_timerBysetup_timerInternal call.

void init_timer( struct timer_list *timer );void setup_timer( struct timer_list *timer,                      void (*function)(unsigned long), unsigned long data );

 

After you have an initialized timer, you need to set the expiration time.mod_timer. Since users usually provide a future expiration time, they usually addjiffiesOffset from the current time. You can also calldel_timerTo delete a timer (if it has not expired ):

int mod_timer( struct timer_list *timer, unsigned long expires );void del_timer( struct timer_list *timer );

 

Finally, you can calltimer_pending(If you are waiting1) To find whether the timer is waiting (not yet issued ):

int timer_pending( const struct timer_list *timer );

 

Timer example

Let's check the actual running status of these API functions. Listing 1 provides a simple kernel module to demonstrate the core features of a simple timer API. Ininit_module, You usesetup_timerInitialize a timer and then callmod_timerTo start it. When the timer expires, the callback function is called.my_timer_callback. Finally, when you delete a module, the timer is deleteddel_timer. (Note thatdel_timerTo check whether the timer is still in use .)

Listing 1. Exploring 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_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;}

 

You can learn more about timer APIs in./include/Linux/Timer. h. Although simple timer APIs are simple and effective, they do not provide the accuracy required by real-time applications. To this end, let's take a look at the new features of Linux, which are used to support timer with higher accuracy.

High-precision timer

High-precision timerHrtimersProvides a high-precision timer management framework independent of the previously discussed timer framework, because merging these two frameworks is too complicated. Although the timerjiffiesRun at the granularity, and hrtimers run at the nanosecond granularity.

The implementation of the hrtimer framework is different from that of the traditional timer API. Instead of using buckets and serial operations, hrtimer maintains a time-ordered timer data structure (inserts a timer in chronological order to minimize the processing during activation ). This data structure is a "red-black" tree that is ideal for performance-oriented applications (and is generally available as a library in the kernel ).

The hrtimer framework is available as an API in the kernel.nanosleep,itimersAnd Portable Operating System Interface (POSIX)-timers interface. The hrtimer framework is made mainlined to the 2.6.21 kernel.

High-precision timer
API

Hrtimer APIs are similar to traditional APIs, but the fundamental difference between them is that they can be used for additional time control. It should be noted that the first point is: Time is not usedjiffiesInsteadktime. This representation hides some details about effective time management at this granularity. The hrtimer API formally confirms the difference between absolute time and relative time, requiring the caller to specify the type.

Similar to the traditional timer API, a high-precision timer is represented by a structure-here ishrtimer. This structure defines the timer (callback function, expiration time, etc.) from the user's perspective and contains management information (the timer exists in the "red-black" tree and optional statistics ).

The definition process first passeshrtimer_initInitialize a timer. This call contains the timer, clock definition, and Timer mode (one-shot or restart ). The clock used is in. /include/Linux/time. h indicates the various clocks supported by the system (for example, real-time clock or single clock, which only indicates the time from a starting point [such as system startup ). After the timer is initialized, you can usehrtimer_startStart. This call contains the expiration time (inktime_t) And time value (absolute or relative value ).

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 hrtimer is started, you can callhrtimer_cancelOrhrtimer_try_to_cancelTo cancel. Each function contains the hrtimer reference of the timer to be stopped. The difference between the two functions is:hrtimer_cancelThe function tries to cancel the timer, but if the timer has been issued, it will wait until the callback function ends;hrtimer_try_to_cancelThe function also tries to cancel the timer, but if the timer has been issued, it returns a failure.

int hrtimer_cancel(struct hrtimer *timer);int hrtimer_try_to_cancel(struct hrtimer *timer);

 

You can callhrtimer_callback_runningTo check whether hrtimer has activated its callback function. Note that this function is composedhrtimer_try_to_cancelInternal call to return an error when the timer callback function is called.

int hrtimer_callback_running(struct hrtimer *timer);

 

Ktime API

This article does not discuss the ktime API. It provides a rich set of functions to manage time with high precision. You can view the ktime API in./Linux/include/ktime. h.

One
Hrtimer example

The hrtimer API is easy to use, as shown in Listing 2. Ininit_moduleFirst, define the relative timeout time (200 ms in this example ). Thenhrtimer_initTo initialize your hrtimer (using a single clock) and set the callback function. Finally, use the previously createdktimeValue to start the timer. When the timer is issuedmy_hrtimer_callbackFunction, which returnsHRTIMER_NORESTARTTo prevent the timer from automatically restarting. Incleanup_moduleFunction, by callinghrtimer_cancelTo cancel the timer.

Listing 2. Exploring 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)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 was still in use...\n");  printk("HR Timer module uninstalling\n");  return;}

 

There is still a lot of content about the hrtimer API that is not involved 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

Kernel list

As described earlier in this article, the list is a useful structure, and the kernel provides an effective general usage implementation. In addition, you will find a list under the APIS we have discussed earlier. The list API that understands this double link helps you use this effective data structure for development. You will find that the code is redundant in the kernel that exploits this list. Now let's take a quick look at this kernel list API.

This API provideslist_headStructure, used to represent the list header (Anchor) and the in-structure list pointer. Let's check the structure of a sample containing the list function (see listing 3 ). Note: In listing 3list_headStructure, which is used for object link ). Note: You can addlist_headStructure-through some GCC (list_entryAndcontainer_of, Defined in./include/kernel. H)-removes the reference from the list pointer to the superobject.

Listing 3. Sample structure with List reference

struct my_data_structure {int value;struct list_head list;};

 

Like other list implementations, a list header is required to act as the list anchor. This usually goes throughLIST_HEADMacro. This macro provides the Declaration and initialization of the list. This macro creates a structurelist_headObject. You can add other objects to the object.

LIST_HEAD( new_list )

 

You can also useLIST_HEAD_INITMacro manually creates a list header (for example, your list header is in another structure ).

After the master Initialization is complete, you can uselist_addAndlist_delAnd other functions to manipulate the list. Next, we will jump to the sample code to better explain how to use this API.

List API
Example

Listing 4 provides a simple kernel module to explore several list of AIO functions (./include/Linux/list. h contains more functions ). In this example, two lists are created.init_moduleFunction to fill them, and then usecleanup_moduleFunction to manipulate the two lists.

At the beginning, you created your data structure (my_data_struct), Which contains some data and two list headers. This example shows how to insert an object to multiple lists at the same time. Then, you create two list headers (my_full_listAndmy_odd_list).

Ininit_moduleFunction, you have created 10 Data Objects and usedlist_addThe function loads them to the list (all objects are loadedmy_full_listAll odd value objects are loadedmy_odd_list). Note the following:list_addTwo parameters are accepted. One is to reference the list in the used object, and the other is the list anchor. This example shows the ability to insert a data object into multiple lists by using the kernel's internal mechanism to identify super objects that contain list references.

cleanup_moduleFunctions provide several other functions of this API, one of which islist_for_eachMacro, which simplifies list iteration. For this macro, you provide a pair (pos) And will be referenced by the iterative list. For each iteration, you receivelist_headReference,list_entryReceive this reference to identify the container object (your data structure ). Specify the list variables in your structure and structure. The latter is used to cancel the reference internally and return the container.

To send a list of surprising values (odd list), use another namelist_for_each_entryIteration macro. This macro is simpler because it automatically provides the data structure and does not need to execute anotherlist_entryFunction.

Finally, uselist_for_each_safeTo release allocated elements. This macro iterates the list, but blocks the deletion of list entries (deleting list entries is part of the iteration operation ). You uselist_entryTo obtain your data object (to release it back to the kernel pool), and then uselist_delTo release entries in the list.

List 4. Exploration list API

#include <linux/kernel.h>#include <linux/module.h>#include <linux/list.h>MODULE_LICENSE("GPL");struct my_data_struct {  int value;  struct list_head full_list;  struct list_head odd_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 < 11 ; count++) {    obj = (struct my_data_struct *)            kmalloc( sizeof(struct my_data_struct), 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 ){  struct list_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 add data at the end of the list instead of the header (list_add_tail), Connection list (list_splice), Test list content (list_empty. For more information about Kernel list functions, see references.

Back to Top

Conclusion

This article explores several APIs to demonstrate the ability to isolate functions (timer API and high-precision hrtimer API) when necessary, as well as the ability to write common code for code reuse (list API ). Traditional timers provide an effective mechanism for typical drivers to time out, while hrtimer provides a higher level of service quality for more precise timer functions. The list API provides an efficient interface that is very common but rich in functions. When you write kernel code, you will encounter one or more such APIs, so they are worth further research.

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.