初始化过程还是要从rild的main入口开始分析起,位于文件hardware/ril/rild/rild.c。简单期间,只介绍本文相关的部分。main()函数会调用函数RIL_register(),然后会调用RIL_startEventLoop()。RIL_startEventLoop()中会创建线程eventLoop,这个实际上就是rild的消息循环所在的线程。
ret = pthread_create(&s_tid_dispatch, &attr, eventLoop, NULL);
eventLoop()首先会进行初始化:ril_event_init();
然后,会通过pipe()函数创建管道。pipe()函数会创建一对文件描述符,一个用于读,另一个用于写。都被记录在全局变量中。它实际上是用来发送定时消息的,后面会介绍。ret = pipe(filedes); // ...... s_fdWakeupRead = filedes[0]; // 消息循环中侦听 s_fdWakeupWrite = filedes[1]; // 用于通知消息循环定义消息已发送
最后一步就是进入消息循环:
// Only returns on error
ril_event_loop();
ril_event_loop()中,每次循环都主要做三件事:
1.初始化。主要是获取要等待的描述符,以及获取超时信息,以便能够及时处理定时消息。
2.等待。等待socket客户端有新的消息,或一定的时间间隔,之后处理定时消息。
3.依次处理三种类型的消息。
下面分别叙述之。先是获取要等待的描述符,这里为什么要这么做呢?我的理解是在添加新的消息时,有可能会修改全局的描述符列表。也就是说,全局的描述符列表有可能在本次循环的处理过程中发生变化。
// make local copy of read fd_set memcpy(&rfds, &readFds, sizeof(fd_set));
接下来,就是计算select()函数的超时参数。select的最后一个参数代表超时。若为NULL,则select死等;若为0,则select立即返回;若大于0,则限制了select最长的等待时间。if (-1 == calcNextTimeout(&tv)) { // 获取超时参数,若失败则返回-1。 // no pending timers; block indefinitely dlog("~~~~ no timers; blocking indefinitely ~~~~"); ptv = NULL; } else { // 获取成功,则使用该值。 dlog("~~~~ blocking for %ds + %dus ~~~~", (int)tv.tv_sec, (int)tv.tv_usec); ptv = &tv; }
calcNextTimeout()函数先是判断超时消息的列表是否为空,为空则返回-1;// Sorted list, so calc based on first node if (tev == &timer_list) { // no pending timers return -1; }