Linux device driver-kernel timer

Kernel timer usage

  

The kernel timer is a mechanism used by the kernel to control the scheduling and execution of a function at a certain point in the future (based on jiffies), and its implementation is located in the < Linux /timer.h> and kernel/timer.c files.

The scheduled function must be executed asynchronously, which is similar to a "software interrupt" and is in the context of a non-process, so the scheduling function must comply with the following rules:

1) There is no current pointer, and access to user space is not allowed. Because there is no process context, the relevant code has no connection with the interrupted process.

2) Sleep (or functions that may cause sleep) and scheduling cannot be performed.

3) Any data structure that is accessed should be protected against concurrent access to prevent race conditions. 

 

After the kernel timer's scheduling function runs once, it will no longer be run (equivalent to automatic logout), but it can be run periodically by rescheduling itself in the scheduled function.

In the SMP system, the scheduling function always runs on the same CPU that registered it, in order to obtain the locality of the cache as much as possible.

 

The data structure of the kernel timer

Copy code

struct timer_list {
    struct list_head entry; 
    unsigned long expires;
    void (*function)(unsigned long);
    unsigned long data; 
    struct tvec_base *base;
    /* ... */
};

Copy code

The expires field represents the jiffies value that the timer is expected to execute. When the jiffies value is reached, the function function will be called and data will be passed as a parameter. When a timer is registered in the kernel, the entry field is used to connect the timer to a kernel linked list. The base field is used by the internal implementation of the kernel.

It should be noted that the value of expires is 32 bits, because the kernel timer is not suitable for long future time points.

initialization

Before using struct timer_list, you need to initialize the data structure to ensure that all fields are set correctly. There are two ways to initialize. 

method one:

DEFINE_TIMER(timer_name, function_name, expires_value, data);

This macro will define a kernel timer named timer_name and initialize its function, expires, name and base fields.

Method Two:

struct timer_list mytimer;
void init_timer(struct timer_list *timer);

The above init_timer function initializes the next entry of the struct timer_list to NULL, without assigning the base pointer 

tm->expires = ;
tm->function = ;
tm->data = ; 

 The setup_timer(&mytimer, (*function)(unsigned long), unsigned long data); method can also be used to initialize the timer and assign values ​​to its members. The source code is:

Copy code

static inline void setup_timer(struct timer_list * timer, void (*function)(unsigned long), unsigned long data)
{
  timer->function = function;
  timer->data = data;
  init_timer (timer);
}

Copy code

Note that no matter which method is used for initialization, its essence is just assigning values ​​to the fields, so as long as the expires, function and data fields can be modified directly before running add_timer().

For the definitions of the above macros and functions, see include/linux/timer.h. 

registered

For the timer to take effect, it must be connected to the kernel's special linked list. This can be achieved by add_timer(struct timer_list *timer).

Re-register (modify)

To modify the scheduling time of a timer, you can call mod_timer(struct timer_list *timer, unsigned long expires). mod_timer() will re-register the timer to the kernel, regardless of whether the timer function has been run.

Logout

To cancel a timer, you can use del_timer(struct timer_list *timer) or del_timer_sync(struct timer_list *timer).

Among them, del_timer_sync is used on SMP systems (on non-SMP systems, it is equal to del_timer). When the timer function to be cancelled is running on another cpu, del_timer_sync() will wait for it to finish running, so this function will Hibernate. In addition, it should avoid competing with the scheduled function for the same lock. For a timer that has been run and has not re-registered itself, the logout function actually has nothing to do. 

int timer_pending(const struct timer_list *timer);

This function is used to determine whether a timer has been added to the kernel linked list to wait to be scheduled to run. Note that when a timer function is about to be run, the kernel will delete the corresponding timer from the kernel linked list (equivalent to logout).

Usage example

Copy code

/* Print a message to the kernel log every second */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/time.h>
#include <linux/timer.h>

static struct timer_list tm;
struct timeval oldtv;

void callback(unsigned long arg)
{
    struct timeval tv;
    char *strp = (char*)arg;
    
    printk("%s: %lu, %s\n", __func__, jiffies, strp);

    do_gettimeofday(&tv);
    printk("%s: %ld, %ld\n", __func__,
        tv.tv_sec-oldtv.tv_sec, //The interval from the last interruption s
        tv.tv_usec- oldtv.tv_usec); // ms interval from the last interrupt
    

    oldtv = tv;
    tm.expires = jiffies+1*HZ;    
    add_timer(&tm); //Restart timing
}

static int __init demo_init(void)
{
    printk(KERN_INFO "%s : %s : %d - ok.\n", __FILE__, __func__, __LINE__);

    init_timer(&tm); //Initialize the kernel timer

    do_gettimeofday(&oldtv); //Get the current time
    tm.function = callback; //Specify the callback function after the time is up
    tm.data = (unsigned long)"hello world"; //The parameters of the callback function
    tm.expires = jiffies+1*HZ; //Timing time
    add_timer(&tm); //register timer

    return 0;
}

static void __exit demo_exit(void)
{
    printk(KERN_INFO "%s : %s : %d - ok.\n", __FILE__, __func__, __LINE__);
    del_timer(&tm); //Logout timer
}

module_init(demo_init);
module_exit(demo_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Farsight");
MODULE_DESCRIPTION("Demo for kernel module");

Copy code

Some time-related content

linux/jiffies.h

Count value:
jiffies
u64 get_jiffies_64(void)

asm/param.h The
number of interrupts triggered per second
HZ

---------------------------------------------

Time value
Number of seconds = (jiffies(new)-jiffies(old))/HZ
jiffies(new) = jiffies(old) + seconds*HZ

---------------------------------------------
linux/delay.h
Delay function
void ssleep(unsigned int seconds);
void msleep(unsigned int msecs);

---------------------------------------------
Time function
linux/time .h
void do_gettimeofday(struct timeval *tv)

Guess you like

Origin blog.csdn.net/daocaokafei/article/details/114805788