最近用了一下alarm定时器,之前有过接触,一直没有怎么整理,所以现在写写,方便以后回来看看。
一、definition:
接触过linux内核和android的应该都不会感到陌生,他是android基于内核rtc实现的一个定时器。
一个硬件定时器,用于把设备从睡眠状态唤醒,定时时间到后可以唤醒系统,此时系统就可以做你想在特定时间要做的事情,基于该定时器还可以实现关机闹铃的功能。
之前也有接触过高精度定时器hrtimer,但是它并不能唤醒系统,所以alarm更像一个外部中断,这是觉得这样理解他的作用就显而易见了。
因为alarm是依赖于rtc实现的,所以很显然他还有一个功能就是掉电后还能正常工作的实时时钟。
二、简单介绍一下RTC:
RTC顾名思义real time clock(实时时钟),他是干什么的呢?
当手机掉电后,再次开机时,手机的时间显示仍然是正常的,就是他的功劳了。
RTC是靠手机电源管理芯片中实现的,他是靠电池供电的实时时钟,所以系统关机后,靠它来记录系统时间;
当系统启动的时候,内核通过读取RTC来初始化墙上时间(当前的实际时间),并将该变量保存在xtime变量中。
系统在运行的过程中,将不再使用RTC,因为他有属于自己的系统定时器。
当系统关机的时候,内核又把当前的时间写入到RTC中。
接下来,就让我们看看该驱动框架。
三、alarm driver :
alarm是在rtc框架上实现的,它主要是由alarm.c和alarm-dev.c实现的。alarm.c实现了所有通用的接口,并且创建了一个设备class,而alarm-dev.c则注册了misc设备,为上层
访问提供了标准的miscdevice接口。也可以这么认为alarm.c实现了机制和框架,而alarm-dev.c实现了这个框架的设备驱动。
先从头文件进行分析:
include/linux/android_alarm.h
48 /** 49 * struct alarm - the basic alarm structure 50 * @node: red black tree node for time ordered insertion 51 * @type: alarm type. rtc/elapsed-realtime/systemtime, wakeup/non-wakeup. 52 * @softexpires: the absolute earliest expiry time of the alarm. 53 * @expires: the absolute expiry time. 54 * @function: alarm expiry callback function 55 * 56 * The alarm structure must be initialized by alarm_init() 57 * 58 */ 59 60 struct alarm { 61 struct rb_node node; 62 enum android_alarm_type type;//android定义的五中alarm类型 63 ktime_t softexpires;//最早的到期时间 64 ktime_t expires;//绝对到期时间 65 void (*function)(struct alarm *);//当到期时间时,系统的回调函数。 66 };
该结构体里面的第一个数据成员是红黑树节点,这个就跟alarm实现的原理有关。alarm会按照到期的先后顺序组织成为一个红黑树。
54 struct alarm_queue { 55 struct rb_root alarms;//表示红黑树的根; 56 struct rb_node *first;//指向第一个到期的alarm设备; 57 struct hrtimer timer;//高精度定时器(内核定时器),马达驱动里面设置震动时间就是利用该定时器实现的;android也是利用它来实现alarm到期时间的; 58 ktime_t delta;//这个参数使用来计算alarm到期时间的一个修正值; 59 bool stopped; 60 ktime_t stopped_time; 61 };
该结构体是在driver/rtc/alarm.c文件中声明的。该结构体的作用就是将alarm表示的设备组织成一颗红黑树。
接下来对alarm.c和alarm-dev.c文件进行分析:
前面有所介绍alarm.c主要是实现了其框架,而alarm-dev.c就是实现了其上层访问接口;
在alarm-dev.c的初始化函数中做了两件事:将其注册为miscdevice设备,为其上层访问提供标准的接口;还有就是对每一个alarm type创建一个alarm设备;并且还初始化了一个
wake lock唤醒锁,当获得该锁时,会阻止系统进入suspend状态;把整个alarm-dev.c读完以后发现,他也是只做了这个三件事。
272 static int __init alarm_dev_init(void) 273 { 274 int err; 275 int i; 276 277 err = misc_register(&alarm_device); 278 if (err) 279 return err; 280 281 for (i = 0; i < ANDROID_ALARM_TYPE_COUNT; i++) 282 alarm_init(&alarms[i], i, alarm_triggered); 283 wake_lock_init(&alarm_wake_lock, WAKE_LOCK_SUSPEND, "alarm"); 284 285 return 0; 286 }
alarm_triggered是alarm时间到时的回调函数,其实现如下:
243 static void alarm_triggered(struct alarm *alarm) 244 { 245 unsigned long flags; 246 uint32_t alarm_type_mask = 1U << alarm->type; 247 248 pr_alarm(INT, "alarm_triggered type %d\n", alarm->type); 249 spin_lock_irqsave(&alarm_slock, flags); 250 if (alarm_enabled & alarm_type_mask) { 251 wake_lock_timeout(&alarm_wake_lock, 5 * HZ); 252 alarm_enabled &= ~alarm_type_mask; 253 alarm_pending |= alarm_type_mask; 254 wake_up(&alarm_wait_queue); 255 } 256 spin_unlock_irqrestore(&alarm_slock, flags); 257 }
当alarm时间到期时,该函数被调用,首先获得一个自旋锁,防止其它alarm发生竞争,然后获得一个超时唤醒锁(5s),wake_up唤醒所有等待在该alarm设备上的进程,
这是AP会对ioctl函数进行操作,即系统调用;这个可以仔细看代码中的alarm_ioctl的swich case部分。
alarm_init函数是在alarm.c中实现的,顾名思义就是初始化一个alarm设备,其函数原型如下:
155 void alarm_init(struct alarm *alarm, 156 enum android_alarm_type type, void (*function)(struct alarm *))
alarm.c分析:
alarm.c主要实现的是底层机制,创建了alarm class这个设备;并且将其注册为platform设备,支持suspend和resume。
首先从alarm_driver_init函数分析:
570 static int __init alarm_driver_init(void) 571 { 572 int err; 573 int i; 574 575 for (i = 0; i < ANDROID_ALARM_SYSTEMTIME; i++) { 576 hrtimer_init(&alarms[i].timer, 577 CLOCK_REALTIME, HRTIMER_MODE_ABS); 578 alarms[i].timer.function = alarm_timer_triggered; 579 } 580 hrtimer_init(&alarms[ANDROID_ALARM_SYSTEMTIME].timer, 581 CLOCK_MONOTONIC, HRTIMER_MODE_ABS); 582 alarms[ANDROID_ALARM_SYSTEMTIME].timer.function = alarm_timer_triggered; 583 err = platform_driver_register(&alarm_driver); 584 if (err < 0) 585 goto err1; 586 wake_lock_init(&alarm_rtc_wake_lock, WAKE_LOCK_SUSPEND, "alarm_rtc"); 587 rtc_alarm_interface.class = rtc_class; 588 err = class_interface_register(&rtc_alarm_interface); 589 if (err < 0) 590 goto err2; 591 592 return 0; 593 594 err2: 595 wake_lock_destroy(&alarm_rtc_wake_lock); 596 platform_driver_unregister(&alarm_driver); 597 err1: 598 return err; 599 }
1、首先,在该init函数中初始化了5个hrtimer高精度定时器,alarm_timer_triggered是其回调函数。注意的是android type有五种类型,在初始化hrtimer定时器时,前四个注册的是CLOCK_REALTIME,而最后一种类型ANDROID_ALARM_SYSTEMTIME注册的是CLOCK_MONOTONIC类型。
2、将其注册为平台设别;
3、初始化了一个alarm_rtc的唤醒锁;
4、注册了classs设备接口;
关于CLOCK_REALTIME和CLOCK_MONOTONIC的区别,网上查了一下,粘贴在下面:
CLOCK_REALTIME:这种类型的时钟可以反映wall clock time,用的是绝对时间,当系统的时钟源被改变,或者系统管理员重置了系统时间之后,这种类型的时钟可以
得到相应的调整,也就是说,系统时间影响这种类型的timer。
CLOCK_MONOTONIC:用的是相对时间,他的时间是通过jiffies值来计算的。该时钟不受系统时钟源的影响,只受jiffies值的影响。
建议使用:
CLOCK_MONOTONIC这种时钟更加稳定,不受系统时钟的影响。如果想反映wall clock time,就使用CLOCK_REALTIME。
之前讲了这么多,好像忘记说alarm定时器是怎么使用的。前面介绍了alarm_init初始化一个定时器,到期后执行其回调函数;那么怎么才能触发一个定时器呢?
这时候就需要alarm_start_range函数了,如果你只有alarm_init 那么alarm是永远无法为你办事的,因为你只是初始化了它,并没有时能它,alarm_start_range就相当于enbale
使能函数。
166 /** 167 * alarm_start_range - (re)start an alarm 168 * @alarm: the alarm to be added 169 * @start: earliest expiry time 170 * @end: expiry time 171 */ 172 void alarm_start_range(struct alarm *alarm, ktime_t start, ktime_t end) 173 { 174 unsigned long flags; 175 176 spin_lock_irqsave(&alarm_slock, flags); 177 alarm->softexpires = start; 178 alarm->expires = end; 179 alarm_enqueue_locked(alarm); 180 spin_unlock_irqrestore(&alarm_slock, flags); 181 }
这里面最重要的就是 alarm_enqueue_locked(alarm);函数,因为它起着计时至关重要的作用。还记得刚刚说到的在alarm 初始化函数中注册了hrtimer定时器吗,他的回调函数
alarm_timer_triggered,但是hrtimer定时器和alarm一样,只是初始化没有使能它,他是不会替你办事的,hrtimer_start后hrtimer定时器被使能,而该使能函数是在update_timer_locked函数中被调用的,而这个update_timer_locked函数很不凑巧是在alarm_enqueue_locked函数中被调用的,这下子明白了吧。所以这也就是为什么一定要alarm_start的原因。
所以很好奇,hrtimer的回调函数到底做了什么呢?那就先看看代码吧:
341 static enum hrtimer_restart alarm_timer_triggered(struct hrtimer *timer) 342 { 343 struct alarm_queue *base; 344 struct alarm *alarm; 345 unsigned long flags; 346 ktime_t now; 347 348 spin_lock_irqsave(&alarm_slock, flags); 349 350 base = container_of(timer, struct alarm_queue, timer); 351 now = base->stopped ? base->stopped_time : hrtimer_cb_get_time(timer); 352 now = ktime_sub(now, base->delta); 353 354 pr_alarm(INT, "alarm_timer_triggered type %d at %lld\n", 355 base - alarms, ktime_to_ns(now)); 356 357 while (base->first) { 358 alarm = container_of(base->first, struct alarm, node); 359 if (alarm->softexpires.tv64 > now.tv64) { 360 pr_alarm(FLOW, "don't call alarm, %pF, %lld (s %lld)\n", 361 alarm->function, ktime_to_ns(alarm->expires), 362 ktime_to_ns(alarm->softexpires)); 363 break; 364 } 365 base->first = rb_next(&alarm->node); 366 rb_erase(&alarm->node, &base->alarms); 367 RB_CLEAR_NODE(&alarm->node); 368 pr_alarm(CALL, "call alarm, type %d, func %pF, %lld (s %lld)\n", 369 alarm->type, alarm->function, 370 ktime_to_ns(alarm->expires), 371 ktime_to_ns(alarm->softexpires)); 372 spin_unlock_irqrestore(&alarm_slock, flags); 373 alarm->function(alarm); 374 spin_lock_irqsave(&alarm_slock, flags); 375 } 376 if (!base->first) 377 pr_alarm(FLOW, "no more alarms of type %d\n", base - alarms); 378 update_timer_locked(base, true); 379 spin_unlock_irqrestore(&alarm_slock, flags); 380 return HRTIMER_NORESTART; 381 }
它会轮询红黑树,谁到了时间符合条件就执行他的回调函数alarm->function,终于整个轮回都通了。
刚刚发现在alarm-dev.c中有一个alarm_triggered的回调函数,在alarm.c中有一个alarm_timer_triggered的回调函数,不过他们是不一样的,
前者是RTC芯片的alarm中断的回调函数,后者是一个具体的alarm定时器到期时的回调函数。
浙公网安备 33010602011771号