https://www.cnblogs.com/chen-farsight/p/6226562.html
linux內核定時器是一個結構體timer_list,定時器掛載在內核定時器鏈表上。
4.1.45版本內核:
struct timer_list {
/*
* All fields that change during normal runtime grouped to the
* same cacheline
*/
struct list_head entry;//定時器列表元素
unsigned long expires;//定時器定時時間
struct tvec_base *base;
void (*function)(unsigned long);//定時器回調函數,定時器時間到時執行該函數
unsigned long data;//作爲參數傳遞給定時器處理函數
int slack;
#ifdef CONFIG_TIMER_STATS
int start_pid;
void *start_site;
char start_comm[16];
#endif
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
};
4.15.1內核版本:
struct timer_list {
/*
* All fields that change during normal runtime grouped to the
* same cacheline
*/
struct hlist_node entry;
unsigned long expires;
void (*function)(struct timer_list *);
u32 flags;
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
};
可以看到timer_list結構體相比較老版本,沒有了data參數。
初始化定時器可以用init_timer()函數,或者timer_setup()函數
init_timer()函數
大爺的,一個init_timer()函數搞這麼複雜,繞這麼多彎。
#define init_timer(timer) \
__init_timer((timer), 0)
#ifdef CONFIG_LOCKDEP
#define __init_timer(_timer, _flags) \
do { \
static struct lock_class_key __key; \
init_timer_key((_timer), (_flags), #_timer, &__key); \
} while (0)
#else
#define __init_timer(_timer, _flags) \
init_timer_key((_timer), (_flags), NULL, NULL)
/**
* init_timer_key - initialize a timer
* @timer: the timer to be initialized
* @flags: timer flags
* @name: name of the timer
* @key: lockdep class key of the fake lock used for tracking timer
* sync lock dependencies
*
* init_timer_key() must be done to a timer prior calling *any* of the
* other timer functions.
*/
void init_timer_key(struct timer_list *timer, unsigned int flags,
const char *name, struct lock_class_key *key)
{
debug_init(timer);
do_init_timer(timer, flags, name, key);
}
static void do_init_timer(struct timer_list *timer, unsigned int flags,
const char *name, struct lock_class_key *key)
{
struct tvec_base *base = raw_cpu_read(tvec_bases);
timer->entry.next = NULL;
timer->base = (void *)((unsigned long)base | flags);
timer->slack = -1;
#ifdef CONFIG_TIMER_STATS
timer->start_site = NULL;
timer->start_pid = -1;
memset(timer->start_comm, 0, TASK_COMM_LEN);
#endif
lockdep_init_map(&timer->lockdep_map, name, key, 0);
}
init_timer()函數中就是base,slack等不重要的亂七八糟的給初始化了。
關於timer_setup()函數,
老版本中是setup_timer():
#define setup_timer(timer, fn, data) \
__setup_timer((timer), (fn), (data), 0)
https://www.cnblogs.com/chen-farsight/p/6226562.html
新版本中setup_timer函數變成了timer_setup函數,timer_setup函數使用見:
https://blog.csdn.net/tiantao2012/article/details/79324154
關於timer_setup宏:
/**
* timer_setup - prepare a timer for first use
* @timer: the timer in question
* @callback: the function to call when timer expires
* @flags: any TIMER_* flags
*
* Regular timer initialization should use either DEFINE_TIMER() above,
* or timer_setup(). For timers on the stack, timer_setup_on_stack() must
* be used and must be balanced with a call to destroy_timer_on_stack().
*/
#define timer_setup(timer, callback, flags) \
__init_timer((timer), (callback), (flags))
#ifdef CONFIG_LOCKDEP
#define __init_timer(_timer, _fn, _flags) \
do { \
static struct lock_class_key __key; \
init_timer_key((_timer), (_fn), (_flags), #_timer, &__key);\
} while (0)
#else
#define __init_timer(_timer, _fn, _flags) \
init_timer_key((_timer), (_fn), (_flags), NULL, NULL)
#endif
其實兩種初始化方法差不多,最後都調用了init_timer_key()函數,只不過timer_setup宏多了一個回調函數參數。
注意,無論用哪種方法初始化,其本質都只是給字段賦值,所以只要在運行 add_timer() 之前,expires, function 和 data 字段都可以直接再修改。
/* 實現每隔一秒向內核log中打印一條信息 */
#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(struct timer_list *arg)
{
struct timeval tv;
printk("--%s: %lu--\n", __func__, jiffies);
do_gettimeofday(&tv);
printk("%s: %ld, %ld\n", __func__,
tv.tv_sec - oldtv.tv_sec, //與上次中斷間隔 s
tv.tv_usec- oldtv.tv_usec); //與上次中斷間隔 ms
oldtv = tv;
tm.expires = jiffies+1*HZ;
add_timer(&tm); //重新開始計時
}
static int __init demo_init(void)
{
printk(KERN_INFO "%s : %s : %d - ok.\n", __FILE__, __func__, __LINE__);
timer_setup(&tm,callback,0);
do_gettimeofday(&oldtv); //獲取當前時間
tm.expires = jiffies+1*HZ; //定時時間
add_timer(&tm); //註冊定時器
return 0;
}
static void __exit demo_exit(void)
{
printk(KERN_INFO "%s : %s : %d - ok.\n", __FILE__, __func__, __LINE__);
del_timer(&tm); //註銷定時器
}
module_init(demo_init);
module_exit(demo_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Farsight");
MODULE_DESCRIPTION("Demo for kernel module");
https://www.iteye.com/blog/allenshao-982270
https://www.jb51.net/article/126465.htm
del_timer(struct timer_list *timer)
註銷一個定時器,可以通過 del_timer(struct timer_list *timer) 或 del_timer_sync(struct timer_list *timer) 。
/**
* del_timer - deactive a timer.
* @timer: the timer to be deactivated
*
* del_timer() deactivates a timer - this works on both active and inactive
* timers.
*
* The function returns whether it has deactivated a pending timer or not.
* (ie. del_timer() of an inactive timer returns 0, del_timer() of an
* active timer returns 1.)
*/
int del_timer(struct timer_list *timer)
{
struct tvec_base *base;
unsigned long flags;
int ret = 0;
debug_assert_init(timer);
timer_stats_timer_clear_start_info(timer);
if (timer_pending(timer)) {
base = lock_timer_base(timer, &flags);
ret = detach_if_pending(timer, base, true);
spin_unlock_irqrestore(&base->lock, flags);
}
return ret;
}
其中 del_timer_sync 是用在 SMP 系統上的(在非SMP系統上,它等於del_timer),當要被註銷的定時器函數正在另一個 cpu 上運行時,del_timer_sync() 會等待其運行完,所以這個函數會休眠。另外還應避免它和被調度的函數爭用同一個鎖。對於一個已經被運行過且沒有重新註冊自己的定時器而言,註銷函數其實也沒什麼事可做。
jiffies:
jiffies是內核中的一個全局變量,用來記錄自系統啓動一來產生的節拍數。譬如,如果計算系統運行了多長時間,可以用 jiffies/tick rate 來計算。
https://blog.csdn.net/qq_33160790/article/details/78660039
可以使用jiffies來定時。
https://www.cnblogs.com/simonshi/archive/2010/12/13/1694819.html