GKI定时器初始化在gki_ulinux.c中的GKI_init中,如下:
void GKI_init(void)
{
......
struct sigevent sigevent;
memset(&sigevent, 0, sizeof(sigevent));
sigevent.sigev_notify = SIGEV_THREAD;
sigevent.sigev_notify_function = (void (*)(union sigval))bt_alarm_cb;
sigevent.sigev_value.sival_ptr = NULL;
if (timer_create(CLOCK_REALTIME, &sigevent, &posix_timer) == -1) {
ALOGE("%s unable to create POSIX timer: %s", __func__, strerror(errno));
timer_created = false;
} else {
timer_created = true;
}
}
这里调用系统API创建了一个定时器,但是此时定时器还没开始计时。这里指定了SIGEV_THREAD表示当定时器到时内核就会创建一个线程执行回调,这里是bt_alarm_cb。
我们可以设想一下,如果我们自己设计一个定时器模块该如何做?首先会有一堆定时任务,我们可以创建很多个timer来分别定时,也可以只创建一个timer来管理所有定时任务。只不过需要将这些任务从近到远排好序,先给timer设置为最近的一个,最近的到时了再设置为下一个最近的,这样一直到所有定时任务都处理完为止。但是GKI的定时结构更复杂一些,因为GKI可以支持多个task,每个task都有多个计时任务队列,每个任务队列都有所不同,比如有的队列计时精度较低,以秒为单位,而有的精度要求较高,以毫秒为单位。这种情况下,GKI采取的方式是轮询,即每个任务队列都有一个轮询周期,在一个轮询结束时去检查队列中有没有超时的任务,有就依次回调这些任务的超时处理。然后再开始下一波轮询。
我们接下来从三个方面来分析GKI:
- 如何启动定时器
- 如何添加定时任务的
- 定时器到时如何处理
先来看看定时器的启动,在gki_time.c中的GKI_start_timer中,如下:
void GKI_start_timer (UINT8 tnum, INT32 ticks, BOOLEAN is_continuous)
{
INT32 reload;
INT32 orig_ticks;
UINT8 task_id = GKI_get_taskid();
if (ticks <= 0)
ticks = 1;
orig_ticks = ticks; /* save the ticks in case adjustment is necessary */
/* If continuous timer, set reload, else set it to 0 */
if (is_continuous)
reload = ticks;
else
reload = 0;
GKI_disable();
/* Add the time since the last task timer update.
** Note that this works when no timers are active since
** both OSNumOrigTicks and OSTicksTilExp are 0.
*/
if (INT32_MAX - (gki_cb.com.OSNumOrigTicks - gki_cb.com.OSTicksTilExp) > ticks)
{
ticks &#