内核定时器timer的使用demo
#include <linux/init.h>
#include <linux/module.h>
#include <linux/timer.h>
#include <linux/rtc.h>
#define SHOW_DL_TIME
#define WAY1
//#define WAY2
//#define WAY3
#ifdef SHOW_DL_TIME
struct timex txc;
struct rtc_time tm;
#endif
#ifdef WAY3
static void timer_function(unsigned long arg);
static DEFINE_TIMER(timer, timer_function, 0, 0);
#else
struct timer_list timer;
#endif
static void timer_function(unsigned long arg)
{
// printk(KERN_INFO "%s\n",__FUNCTION__);
mod_timer(&timer, jiffies + 2*HZ); //重新开始计时
#ifdef SHOW_DL_TIME
do_gettimeofday(&(txc.time));
rtc_time_to_tm(txc.time.tv_sec,&tm);
pr_err("%s#########:: %d-%d-%d %d:%d:%d \n", __func__, tm.tm_year+1900,tm.tm_mon, tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec);
#endif
}
static int timer_init(void)
{
printk(KERN_INFO "%s\n",__FUNCTION__);
#if defined(WAY1)
init_timer(&timer);
timer.data=100;
timer.function=timer_function;
#elif defined(WAY2)
setup_timer(&timer, timer_function, 100);
#else
#endif
timer.expires=jiffies+2*HZ;
add_timer(&timer);
return 0;
}
static void timer_exit(void)
{
printk(KERN_INFO "%s\n",__FUNCTION__);
del_timer(&timer);
}
module_init(timer_init);
module_exit(timer_exit);
MODULE_LICENSE("Dual BSD/GPL");
内核定时器有以上三种方法来初始化,通过定义不同的宏使用不同方法,当定时时间到,打印相应的时间,通过对比两次打印,可知道定时的时间。
工作原理
软中断类型
enum
{
HI_SOFTIRQ=0,
TIMER_SOFTIRQ,
NET_TX_SOFTIRQ,
NET_RX_SOFTIRQ,
BLOCK_SOFTIRQ,
BLOCK_IOPOLL_SOFTIRQ,
TASKLET_SOFTIRQ,
SCHED_SOFTIRQ,
HRTIMER_SOFTIRQ, /* Unused, but kept as tools rely on the numbering. Sigh! */
RCU_SOFTIRQ, /* Preferable RCU should always be the last softirq */
NR_SOFTIRQS
};
内核初始化设立TIMER_SOFTIRQ的处理函数run_timer_softirq
void __init init_timers(void)
{
open_softirq(TIMER_SOFTIRQ, run_timer_softirq);
}
void open_softirq(int nr, void (*action)(struct softirq_action *))
{
softirq_vec[nr].action = action;
}
看下run_timer_softirq,找出超时的定时器,运行其超时处理函数
static void run_timer_softirq(struct softirq_action *h)
{
struct tvec_base *base = this_cpu_ptr(&tvec_bases);
if (time_after_eq(jiffies, base->timer_jiffies))
__run_timers(base);
}
static inline void __run_timers(struct tvec_base *base)
{
while (!hlist_empty(head)) {
void (*fn)(unsigned long);
unsigned long data;
bool irqsafe;
timer = hlist_entry(head->first, struct timer_list, entry);
fn = timer->function;
data = timer->data;
call_timer_fn(timer, fn, data);
}
static void call_timer_fn(struct timer_list *timer, void (*fn)(unsigned long),unsigned long data)
{
fn(data);
}
那在什么时机处理定时器的超时呢
//ARM generic timer(arm_timer 用于更新时间/进程调度/高精度定时器唤醒等)
在arm_timer中断发生后,运行run_local_timers,标志有TIMER_SOFTIRQ发生
void run_local_timers(void)
{
raise_softirq(TIMER_SOFTIRQ);
}
void __raise_softirq_irqoff(unsigned int nr)
{
trace_softirq_raise(nr);
or_softirq_pending(1UL << nr);//设置相应的软中断标志位,这里是TIMER_SOFTIRQ
}
void irq_enter(void)
{
preempt_count_add(HARDIRQ_OFFSET);
}
/*
* This function must run with irqs disabled!
*/
inline void raise_softirq_irqoff(unsigned int nr)
{
__raise_softirq_irqoff(nr);
/*
* If we're in an interrupt or softirq, we're done
* (this also catches softirq-disabled code). We will
* actually run the softirq once we return from
* the irq or softirq.
*
* Otherwise we wake up ksoftirqd to make sure we
* schedule the softirq soon.
*/
if (!in_interrupt()) //in_interrupt()==true
wakeup_softirqd();
}
void raise_softirq(unsigned int nr)
{
unsigned long flags;
local_irq_save(flags);
raise_softirq_irqoff(nr);
local_irq_restore(flags);
}
中断后面会执行
void irq_exit(void)
{
preempt_count_sub(HARDIRQ_OFFSET);
if (!in_interrupt() && local_softirq_pending())//in_interrupt()==false
invoke_softirq();
}
static inline void invoke_softirq(void)
{
do_softirq_own_stack();
}
static inline void do_softirq_own_stack(void)
{
__do_softirq();
}
asmlinkage __visible void __do_softirq(void)
{
__u32 pending;
int softirq_bit;
pending = local_softirq_pending();
h = softirq_vec;
while ((softirq_bit = ffs(pending))) {
unsigned int vec_nr;
h += softirq_bit - 1;
vec_nr = h - softirq_vec;
h->action(h);//调用TIMER_SOFTIRQ的处理函数run_timer_softirq
}