内核和进程广泛使用定时器。大多数设备驱动程序利用定时器检测反常情况,例如,软盘驱动程序使用定时器在软盘暂时不被访问后就关闭设备的引擎,而并行打印机设备利用定时器检测错误的打印机情况。
程序员也经常利用定时器在将来某一时刻强制执行特定的函数。
相对来说,实现一个定时器并不难。每个定时器都包含一个字段,表示定时器将需要多长时间才到期。这个字段的初值就是jiffies的当前值加上合适的节拍数。这个字段的值将不再改变。(包含了表示当前时刻的自增变量jiffies,这个字段的值怎么保持不变的呢?)每当内核检查定时器时,就把这个到期字段值和当前这一刻jiffies的值相比较,当jiffies大于或等于这个字段存放的值时,定时器到期。
警告:因为对定时器函数的检查总是由可延迟函数进行,而可延迟函数被激活以后很长时间才能被执行,因此,内核不能确保定时器函数正好在定时到期时就开始执行,只能保证在适当的时间执行它们,或者比如延迟到几百毫秒之后执行它们。因此,对于必须严格遵守定时时间的那些实时应用而言,定时器并不合适。
linux系统考虑两种类型的定时器,即动态定时器(dynamic timer)和间隔定时器(interval timer)。第一种类型由内核使用,而间隔定时器可以由进程在用户在创建。
(摘自《深入理解linux内核第三版》)
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
};
#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)
#define __init_timer_on_stack(_timer, _flags) \
do { \
static struct lock_class_key __key; \
init_timer_on_stack_key((_timer), (_flags), #_timer, &__key); \
} while (0)
#else
#define __init_timer(_timer, _flags) \
init_timer_key((_timer), (_flags), NULL, NULL)
#define __init_timer_on_stack(_timer, _flags) \
init_timer_on_stack_key((_timer), (_flags), NULL, NULL)
#endif
/**
- 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);
}
EXPORT_SYMBOL(add_timer);
/**
- timer_pending - is a timer pending?
- @timer: the timer in question
- - timer_pending will tell whether a given timer is currently pending or not. Callers must ensure serialization wrt. other operations done to this timer, eg. interrupt contexts, or other CPUs on SMP.
- - return value: 1 if the timer is pending, 0 if not.
*/
static inline int timer_pending(const struct timer_list * timer)
{
return timer->entry.next != NULL;
}
功能:向内核注册添加一个定时器,一旦添加完,定时器就开始倒计时一旦时间到期,内核就会调用其超时处理函数并且将定时器从内核中删除,所以内核定时器的超时处理函数只执行一次
参数: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;
}
EXPORT_SYMBOL(del_timer);
/**
* 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() 是一种有效的方式去更新一个活跃的时间的超时时间,如果这个定时器是不活跃的,将被激活
* mod_timer(timer, expires) is equivalent to:
* del_timer(timer); timer->expires = expires; add_timer(timer);
*mod_timer(timer, expires) 函数等价于先删除软件定时器,再设置一个定时器超时时间,然后再添加这个软件定时器
* 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.
*如果同一个定时器有多个未序列化的并发访问用户,那么mod_timer()函数是唯一修改超时的安全方法,因为add_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.)
* mod_timer()函数返回1:如果修改一个已经被激活的定时器(即这个定时器已经被挂起)
* mod_timer()函数返回0:如果修改一个没有被激活的定时器(即这个定时器没有被挂起)
*/
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);
//头文件
#include
#include
#include
#include
#include
/*其他
//声明描述LED的硬件信息的数据结构
//定义初始化4个LED硬件信息对象
*/
//定义定时器对象
static struct timer_list mytimer;
//定时器的超时处理函数
//定时器超时时间到期,内核执行此函数
//切记:千万不能进行休眠操作
//data = (unsigned long)&g_data
static void mytimer_function(unsigned long data)
{
printk("%s: data = %#x\n", __func__, *(int *)data);
//2.重新向内核添加定时器对象
/*以下两条语句相当之危险,因为他们访问了一个全局变量mytimer,如果将来有高优先级的硬件中断和高优先级的软中断来打断
* 这两条语句的执行,势必造成定时器的错误,需要考虑互斥问题
mytimer.expires = jiffies + 2*HZ; //重新添加新的超时时间
add_timer(&mytimer);
*/
//del_timer+expire=...+add_timer
//此函数非常安全,因为里面做了互斥访问机制
mod_timer(&mytimer, jiffies+2*HZ);
}
/*
static irqreturn_t button_isr(int irq, void *dev)
{
//此代码会篡改定时器的超时时间
mytimer.expires = jiffies + 50*HZ;
return IRQ_HANDLED;
}
*/
static int g_data = 0x5555;
static int mytimer_init(void)
{
//1.申请GPIO资源,配置GPIO为输出,输出1
//2.初始化定时器对象
init_timer(&mytimer);
//3.额外指定定时器的超时时间
mytimer.expires = jiffies + 2*HZ;
//4.额外指定定时器的超时处理函数
mytimer.function = mytimer_function;
//5.额外指定给超时处理函数传递的参数
mytimer.data = (unsigned long)&g_data;
//6.向内核添加定时器对象,开始倒计时
//一旦时间到期,内核调用超时处理函数并且删除定时器
add_timer(&mytimer);
return 0;
}
static void mytimer_exit(void)
{
//1.释放GPIO资源
//2.删除定时器对象
del_timer(&mytimer);
}
module_init(mytimer_init);
module_exit(mytimer_exit);
MODULE_LICENSE("GPL");
案例 :利用定时器,实现每隔 2 秒钟开关某个LED灯
//头文件
#include
#include
#include
#include
#include
#include
#include
#include
//声明描述LED灯硬件信息的数据结构
struct led_resource{
int gpio;
char* name;
};
//定义初始化4个LED灯的硬件信息
struct led_resource led_info[] = {
{PAD_GPIO_C+12, "LED1"},
{PAD_GPIO_C+7, "LED2"},
{PAD_GPIO_C+11, "LED3"},
{PAD_GPIO_B+26, "LED4"}
};
static int g_data; //定义该全局变量给软件定时器处理函数传递控制哪个led灯信息
//定义一个软件定时器对象
static struct timer_list vtimer;
//软件定时器的处理函数
static void vtimer_func(unsigned long data){
printk("%s:data = %#x\n",__func__, *(int *)data);
int state;
state = gpio_get_value(led_info[g_data].gpio);
gpio_set_value(led_info[g_data].gpio, !state);
mod_timer(&vtimer, jiffies + 2*HZ);
}
#if 1
#define LED_ON 0X10001
#define LED_OFF 0X10002
//led_ioctl
static long led_ioctl(struct file* file, int cmd, unsigned long arg){
//分配内核缓冲区
copy_from_user(&g_data, (int *)arg, sizeof(g_data));
switch(cmd){
case LED_ON:
// gpio_set_value(led_info[kindex-1].gpio, 0);
// printk("%s:开第%d个灯\n", __func__, kindex);
init_timer(&vtimer);
vtimer.expires = jiffies + 2*HZ;
vtimer.function = vtimer_func;
vtimer.data = (unsigned long)&g_data;
add_timer(&vtimer);
break;
case LED_OFF:
// gpio_set_value(led_info[kindex-1].gpio, 1);
// printk("%s:关第%d个灯\n",__func__, kindex);
break;
default:
printk("%s:无效操作\n",__func__);
return -1;
}
return 0;
}
//定义初始化LED的硬件操作接口
static struct file_operations led_fops = {
.unlocked_ioctl = led_ioctl,
.owner = THIS_MODULE
};
//定义初始化led的混杂设备对象
struct miscdevice led_misc = {
.minor = MISC_DYNAMIC_MINOR,
.fops = &led_fops,
.name = "vaccine_misc"
};
#endif
//入口函数
static int led_drv_init(void){
//申请gpio资源
int i;
for(i = 0; i < ARRAY_SIZE(led_info); i++){
gpio_request(led_info[i].gpio, led_info[i].name);
gpio_direction_output(led_info[i].gpio, 1);
}
//向内核注册一个混杂设备对象,内核会自动创建一个名称为name的设备文件,
//并且会自动分配一个次设备号
misc_register(&led_misc);
return 0;
}
//出口函数
static void led_drv_exit(void){
//释放gpio资源
int i;
for(i = 0; i < ARRAY_SIZE(led_info); i++){
gpio_set_value(led_info[i].gpio, 1);
gpio_free(led_info[i].gpio);
}
//从内核卸载一个混杂设备对象,内核会删除创建的设备文件
//且内核会释放掉申请的次设备号
misc_deregister(&led_misc);
del_timer(&vtimer);
}
//各种修饰和GPL规则
module_init(led_drv_init);
module_exit(led_drv_exit);
MODULE_LICENSE("GPL");
在软件定时器的超时处理函数中:如果按照如下的写法
//定时器的超时处理函数
//定时器超时时间到期,内核执行此函数
//切记:千万不能进行休眠操作
//data = (unsigned long)&g_data
static void mytimer_function(unsigned long data)
{
printk("%s: data = %#x\n", __func__, *(int *)data);
add_timer(&mytimer);
}
以上会导致两个问题: