赞
踩
内核和进程广泛使用定时器。大多数设备驱动程序利用定时器检测反常情况,例如,软盘驱动程序使用定时器在软盘暂时不被访问后就关闭设备的引擎,而并行打印机设备利用定时器检测错误的打印机情况。
程序员也经常利用定时器在将来某一时刻强制执行特定的函数。
相对来说,实现一个定时器并不难。每个定时器都包含一个字段,表示定时器将需要多长时间才到期。这个字段的初值就是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 <linux/init.h> #include <linux/module.h> #include <linux/timer.h> #include <linux/gpio.h> #include <mach/platform.h> /*其他 //声明描述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 <linux/init.h> #include <linux/module.h> #include <linux/timer.h> #include <linux/miscdevice.h> #include <linux/fs.h> #include <linux/uaccess.h> #include <linux/gpio.h> #include <mach/platform.h> //声明描述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);
}
以上会导致两个问题:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。