Linux 內核定時器 一 函數簡介

Linux 內核定時器 一 函數簡介

Linux 內核定時器 二 例子demo

 

我們在編寫內核驅動的時候,有時候需要掛載驅動的一段時間後執行某項任務,或者週期行的執行某項任務。

這種時候需要藉助內核定時器來協助我們。(類似“軟件中斷”)

定時器調用流程:

1. 初始化

    a. 宏 init_timer

    b. 宏DEFINE_TIMER

    c. 宏  setup_timer

2.向內核中添加定時器

     a.  add_timer - 初始化完成後,向內核中添加定時器

     b. mod_timer - 如果需要週期性執行任務,在定時器回調函數中添加 mod_timer

3. 手動/自動釋放

     a. 定時器運行後,不會再運行,類似自動註銷;

     b.  定時器未運行,可手動釋放 -  del_timer

 

源碼:  linux-3.10.y

struct timer_list

// linux-3.10.y/include/linux\timer.h
struct timer_list
{
    /*
     * All fields that change during normal runtime grouped to the
     * same cacheline
     */
    struct list_head entry; // 定時器列表
    unsigned long expires;  // 期望定時器執行的jiffies值
    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
};

重點 :

  • expires,定時器的到期時間,單位是jiffies 
  • function,定時器到期,要執行的函數 
  • data,傳入要執行的函數的參數

 

初始化定時器 - 三種方法

方法一 : 宏 init_timer  初始化 struct timer_list 

init_timer(timer)  // 傳參 time 是 struct timer_list 指針

// linux-3.10.y/include/linux\timer.h
// 功能是初始化 timer_list 結構體的 entry 的next 爲 NULL,並給 base 指針賦值
#define init_timer(timer)                      \
    __init_timer((timer), 0)
 
// linux-3.10.y/kernel/timer.c
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);
}
EXPORT_SYMBOL(init_timer_key);
 
static inline void debug_init(struct timer_list *timer)
{
    debug_timer_init(timer);
    trace_timer_init(timer);
}
 
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_get_cpu_var(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);
}

 

方法二 :宏DEFINE_TIMER 初始化 struct timer_list

DEFINE_TIMER 中創建 struct timer_list 型變量,無需再外部定義 timer_list 變量

// linux-3.10.y/include/linux\timer.h
#define __TIMER_INITIALIZER(_function, _expires, _data, _flags) { \
		.entry = { .prev = TIMER_ENTRY_STATIC },	\
		.function = (_function),			\
		.expires = (_expires),				\
		.data = (_data),				\
		.base = (void *)((unsigned long)&boot_tvec_bases + (_flags)), \
		.slack = -1,					\
		__TIMER_LOCKDEP_MAP_INITIALIZER(		\
			__FILE__ ":" __stringify(__LINE__))	\
	}

#define TIMER_INITIALIZER(_function, _expires, _data)		\
	__TIMER_INITIALIZER((_function), (_expires), (_data), 0)

#define TIMER_DEFERRED_INITIALIZER(_function, _expires, _data)	\
	__TIMER_INITIALIZER((_function), (_expires), (_data), TIMER_DEFERRABLE)

#define DEFINE_TIMER(_name, _function, _expires, _data)		\
	struct timer_list _name =				\
		TIMER_INITIALIZER(_function, _expires, _data)

 

方法三 :宏  setup_timer  初始化 struct timer_list

setup_timer 和 init_timer 類似。

// linux-3.10.y/include/linux\timer.h
#define __setup_timer(_timer, _fn, _data, _flags)           \
    do {                               \
        __init_timer((_timer), (_flags));          \
        (_timer)->function = (_fn);                \
        (_timer)->data = (_data);              \
    } while (0)
 
#define __setup_timer_on_stack(_timer, _fn, _data, _flags)      \
    do {                               \
        __init_timer_on_stack((_timer), (_flags));     \
        (_timer)->function = (_fn);                \
        (_timer)->data = (_data);              \
    } while (0)
 
#define setup_timer(timer, fn, data)                 \
    __setup_timer((timer), (fn), (data), 0)

 

增加/修改定時器

先看函數聲明:

void add_timer(struct timer_list *timer)   // 增加定時器, 定時器運行後,自動釋放
int mod_timer(struct timer_list *timer, unsigned long expires) // 重設,增加定時器

源碼如下:

// linux-3.10.y/kernel/timer.c
// 註冊內核定時器,將定時器加入到內核動態定時器鏈表,即啓動定時器
void add_timer(struct timer_list *timer)
{
    BUG_ON(timer_pending(timer));
    mod_timer(timer, timer->expires);
}
EXPORT_SYMBOL(add_timer);
 
// 重設延時時間,啓動定時器
int mod_timer(struct timer_list *timer, unsigned long expires)
{
    expires = apply_slack(timer, expires);
 
    /*
     * This is a common optimization triggered by the
     * networking code - if the timer is re-modified
     * to be the same thing then just return:
     */
    if(timer_pending(timer) && timer->expires == expires)
        return 1;
 
    return __mod_timer(timer, expires, false, TIMER_NOT_PINNED);
}
EXPORT_SYMBOL(mod_timer);
 
 
static inline int
__mod_timer(struct timer_list *timer, unsigned long expires,
            bool pending_only, int pinned)
{
    struct tvec_base *base, *new_base;
    unsigned long flags;
    int ret = 0, cpu;
 
    timer_stats_timer_set_start_info(timer);
    BUG_ON(!timer->function);
 
    base = lock_timer_base(timer, &flags);
 
    ret = detach_if_pending(timer, base, false);
    if(!ret && pending_only)
        goto out_unlock;
 
    debug_activate(timer, expires);
 
    cpu = smp_processor_id();
 
#if defined(CONFIG_NO_HZ_COMMON) && defined(CONFIG_SMP)
    if(!pinned && get_sysctl_timer_migration() && idle_cpu(cpu))
        cpu = get_nohz_timer_target();
#endif
    new_base = per_cpu(tvec_bases, cpu);
 
    if(base != new_base)
    {
        /*
         * We are trying to schedule the timer on the local CPU.
         * However we can't change timer's base while it is running,
         * otherwise del_timer_sync() can't detect that the timer's
         * handler yet has not finished. This also guarantees that
         * the timer is serialized wrt itself.
         */
        if(likely(base->running_timer != timer))
        {
            /* See the comment in lock_timer_base() */
            timer_set_base(timer, NULL);
            spin_unlock(&base->lock);
            base = new_base;
            spin_lock(&base->lock);
            timer_set_base(timer, base);
        }
    }
 
    timer->expires = expires;
    internal_add_timer(base, timer);
 
out_unlock:
    spin_unlock_irqrestore(&base->lock, flags);
 
    return ret;
}

 

刪除定時器

函數聲明:

// 直接刪除
int del_timer(struct timer_list * timer); 

源碼:

// linux-3.10.y/kernel/timer.c
/**
 * 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;
}
EXPORT_SYMBOL(del_timer);

 

 

發佈了51 篇原創文章 · 獲贊 14 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章