最近用了一下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定时器到期时的回调函数。