概述
前言
头文件:
#include <linux/timer.h>
一 内核定时器 timer_list
软件意义上的定时器最终依赖硬件定时器来实现,内核在时钟中断发生后检测各定时器是否到期,到期后的定时器处理函数将作为软中断在底半部执行。实质上,时钟中断处理程序会唤起TIMER_SOFTIRQ软中断,运行当前处理器上到期的所有定时器。
在Linux设备驱动编程中,可以利用Linux内核中提供的一组函数和数据结构来完成定时触发工作或者完成某周期性的事务。这组函数和数据结构使得驱动工程师在多数情况下不用关心具体的软件定时器究竟对应着怎样的内核和硬件行为。
Linux内核所提供的用于操作定时器的数据结构和函数如下。
1.timer_list
在Linux内核中,timer_list结构体的一个实例对应一个定时器,如下
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
}
当定时器期满后,其中第10行的function()成员将被执行,而data成员则是传入其中的参数,expires则是定时器到期的时间(jiffies)。如下代码定义一个名为my_timer的定时器:
struct timer_list my_timer;
2.初始化定时器
init_timer是一个宏,它的原型如下:timer是一个指针,例如:struct timer_list * timer。
#define init_timer(timer)
__init_timer((timer), 0)
上述init_timer()函数初始化timer_list的entry的next为NULL,并给base指针赋值。TIMER_INITIALIZER(_function,_expires,_data)宏用于赋值定时器结构体的function、expires、data和base成员,这个宏原型:
#define TIMER_INITIALIZER(_function, _expires, _data)
__TIMER_INITIALIZER((_function), (_expires), (_data), 0)
#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(_name,_function,_expires,_data)宏是定义并初始化定时器成员的“快捷方式”,这个宏定义为:
#define DEFINE_TIMER(_name, _function, _expires, _data)
struct timer_list _name =
TIMER_INITIALIZER(_function, _expires, _data)
此外,setup_timer()也可用于初始化定时器并赋值其成员,其源代码为:
#define setup_timer(timer, fn, data)
__setup_timer((timer), (fn), (data), 0)
#define __setup_timer(_timer, _fn, _data, _flags)
do {
__init_timer((_timer), (_flags));
(_timer)->function = (_fn);
(_timer)->data = (_data);
} while (0)
3.增加定时器add_timer
从函数原型可知add_timer是通过调用mod_timer实现的。所以啊,就不要拼命计较用add_timer还是用mod_timer啦。
extern void add_timer(struct timer_list *timer);
/**
* add_timer - start a timer
* @timer: the timer to be added
*
* The kernel will do a ->function(->data) callback from the
* timer interrupt at the ->expires point in the future. The
* current time is 'jiffies'.
*
* The timer's ->expires, ->function (and if the handler uses it, ->data)
* fields must be set prior calling this function.
*
* Timers with an ->expires field in the past will be executed in the next
* timer tick.
*/
void add_timer(struct timer_list *timer)
{
BUG_ON(timer_pending(timer));
mod_timer(timer, timer->expires);
}
4.删除定时器del_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函数用于删除定时器。del_timer_sync()是del_timer()的同步版,在删除一个定时器时需等待其被处理完,因此该函数的调用不能发生在中断上下文中。
5.修改定时器的expir:mod_timer
函数mod_timer用于修改定时器的到期时间,在新的被传入的expires到来后才会执行定时器函数。
/**
* mod_timer - modify a timer's timeout
* @timer: the timer to be modified
* @expires: new timeout in jiffies
*
* mod_timer() is a more efficient way to update the expire field of an
* active timer (if the timer is inactive it will be activated)
*
* mod_timer(timer, expires) is equivalent to:
*
* del_timer(timer); timer->expires = expires; add_timer(timer);
*
* Note that if there are multiple unserialized concurrent users of the
* same timer, then mod_timer() is the only safe way to modify the timeout,
* since add_timer() cannot modify an already running timer.
*
* The function returns whether it has modified a pending timer or not.
* (ie. mod_timer() of an inactive timer returns 0, mod_timer() of an
* active timer returns 1.)
*/
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);
}
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 = get_nohz_timer_target(pinned);
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;
}
二 测试实例
1 定时器到期时间设置方法
1)延时一秒expires = jiffies + Hz;
unsigned long expires = jiffies + Hz;
mod_timer(&timer,expires);
2)延时 100毫秒
unsigned long expires = jiffies + msecs_to_jiffies(100);
mod_timer(&timer,expires);
2 测试代码:csi_timer.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/timer.h>
#define DEBUG_CT(format,...)
printk("%s:%s:%d: "format"n",
__FILE__,__func__,__LINE__,
##__VA_ARGS__)
struct ct_dev_{
struct timer_list ct_timer;
int count;
};
struct ct_dev_ *ct_dev;
static void ct_timer_function(unsigned long data)
{
struct ct_dev_ *p = (struct ct_dev_ *)data;
DEBUG_CT("%d",p->count++);
if(p->count < 5){
DEBUG_CT("HZ=%d,msecs_to_jiffies(1000)=%lu",HZ,msecs_to_jiffies(1000));
mod_timer(&p->ct_timer,jiffies + HZ);
}
}
static int __init ct_init(void)
{
struct ct_dev_ *p = NULL;
ct_dev = (struct ct_dev_ *)kmalloc(sizeof(struct ct_dev_),GFP_KERNEL);
if(IS_ERR(ct_dev)){
DEBUG_CT("kmalloc error");
return -ENOMEM;
}
p = ct_dev;
setup_timer(&p->ct_timer,ct_timer_function,(unsigned long)p);
p->count = 0;
mod_timer(&p->ct_timer,HZ);
DEBUG_CT("init ok");
return 0;
}
static void __exit ct_exit(void)
{
struct ct_dev_ *p = ct_dev;
if(IS_ERR(p)){
return;
}
del_timer_sync(&p->ct_timer);
kfree(p);
DEBUG_CT("exit ok");
}
module_init(ct_init);
module_exit(ct_exit);
MODULE_LICENSE("GPL");
3 Makefile文件:
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
KERNELDIR := /home/lkmao/imx/linux/linux-imx
CURRENT_PATH := $(shell pwd)
FILE_NAME=csi_timer
obj-m := $(FILE_NAME).o
build: kernel_modules
kernel_modules:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
sudo cp $(FILE_NAME).ko /big/nfsroot/jiaocheng_rootfs/home/root/
clean:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
4 执行结果
从输出结果可知当前HZ的值是100。这意味着什么,暂时还不用考虑。知道它放在这里表示1秒就行,msecs_to_jiffies(1000)=100也等于100,100个jiffies单位。表示1000毫秒。
root@hehe:~# insmod csi_timer.ko
[ 5426.520022] /big/csi_driver/csi_timer/csi_timer.c:ct_init:40: init ok
[ 5426.526598] /big/csi_driver/csi_timer/csi_timer.c:ct_timer_function:21: 0
[ 5426.526606] /big/csi_driver/csi_timer/csi_timer.c:ct_timer_function:23: HZ=100,msecs_to_jiffies(1000)=100
root@hehe:~# [ 5427.523293] /big/csi_driver/csi_timer/csi_timer.c:ct_timer_function:21: 1
[ 5427.530110] /big/csi_driver/csi_timer/csi_timer.c:ct_timer_function:23: HZ=100,msecs_to_jiffies(1000)=100
[ 5428.523293] /big/csi_driver/csi_timer/csi_timer.c:ct_timer_function:21: 2
[ 5428.530110] /big/csi_driver/csi_timer/csi_timer.c:ct_timer_function:23: HZ=100,msecs_to_jiffies(1000)=100
[ 5429.523295] /big/csi_driver/csi_timer/csi_timer.c:ct_timer_function:21: 3
[ 5429.530113] /big/csi_driver/csi_timer/csi_timer.c:ct_timer_function:23: HZ=100,msecs_to_jiffies(1000)=100
[ 5430.523298] /big/csi_driver/csi_timer/csi_timer.c:ct_timer_function:21: 4
结束
最后
以上就是俭朴龙猫为你收集整理的linux内核定时器:timer_list前言 一 内核定时器 timer_list二 测试实例结束的全部内容,希望文章能够帮你解决linux内核定时器:timer_list前言 一 内核定时器 timer_list二 测试实例结束所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复