linux內核定時器

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

https://blog.csdn.net/u011387543/article/details/80351384

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