分享

浅析Android的RILD服务进程的消息循环

 mot0522 2013-02-21

浅析Android的RILD服务进程的消息循环  

2012-01-11 15:36:27|  分类: Android开发 |  标签:android  消息   |字号 订阅

Android中,RILD是RIL(Radio Interface Layer) Deamon的简称。简单的说它下面承接GSM/GPRS Modem(电话通信模块),上面接电话应用相关的Java库(telephony internal)。telephony internal通过socket将请求发送给RILD的消息循环,消息循环则将请求转发给底层通信模块(直接调用底层的库)来实现对通信模块功能的调 用。反之,当通信模块有类似于来电的消息时,也会通过RILD的回调,将信息包装成消息,发送到RILD的消息循环中去处理,最后再通过socket回送 给telephony internal,以便通知上层。整体结构参见如下:


  • 概述 

        消息循环(ril_event_loop()函数内)位于单独的线程中,用来处理三种消息,分别对应三个不同的队列,面对的是三种不同的需求:
        1.定时列表(timer_list): 此队列中的消息主要用于处理一些延时的操作。比如:从飞信模式切换到通信模式(实际上就是打开通信模块)时,若SIM卡未准备好,那么需要延续一段时间再检查是否准备好,此时就要将消息扔至此队列。
        2.侦听列表(watch_list): 此队列中的消息一是作为socket的服务端,用来侦听客户端的请求;另一个是作为本进程的其它线程(如:检查通信模块来电消息的线程)传递过来的消息。
        3.挂起列表(pending_list): 之所以叫挂起,实际上指的是在处理上面两中类型的消息时,并不真正的处理消息体,而是将符合条件的消息丢到本队列中。由于消息附带处理函数,所以在处理本队列的消息时,直接触发即可。
        消息循环所要做的事就是先等待某个时间间隔(定时消息要求的)或者是客户端(socket或通讯模块的消息)的请求,然后再按次序处理上面三个对列中的消息。

        下面还是通过源代码来分析消息循环的处理过程。

  • ril_event

        ril_event指代的是消息,它的数据结构如下:

  1. struct ril_event *next; // 下一个消息   
  2. struct ril_event *prev; // 前一个消息   
  3. int fd; // 开始我以为这里有可能是socket描述符或pipe描述符,但实际上它还被用作消息的回调函数的参数?   
  4. int index; // 侦听列表(watch_list)中的索引,侦听列表采用的数组而不是链表   
  5. bool persist; // 是否常驻侦听列表(watch_list)中。若常驻,则当被转到pending_list中时,不删除。   
  6. struct timeval timeout; // 超时,这个为一个时间点。在处理定时消息时,用于和当前的时间比较,以确定是否处理该消息。   
  7. ril_event_cb func; // 回调。此消息对应的处理函数。   
  8. void *param; // 回调参数。  
        三种消息列表的定义如下:
  1. static struct ril_event * watch_table[MAX_FD_EVENTS]; // 侦听列表   
  2. static struct ril_event timer_list; // 定时列表   
  3. static struct ril_event pending_list; // 挂起列表  
  • ril_event_init()

        初始化过程还是要从rild的main入口开始分析起,位于文件hardware/ril/rild/rild.c。简单期间,只介绍本文相关的部分。 main()函数会调用函数RIL_register(),然后会调用RIL_startEventLoop()。 RIL_startEventLoop()中会创建线程eventLoop,这个实际上就是rild的消息循环所在的线程。

  1. ret = pthread_create(&s_tid_dispatch, &attr, eventLoop, NULL);  
        eventLoop()首先会进行初始化:
  1. ril_event_init();  
        然后,会通过pipe()函数创建管道。pipe()函数会创建一对文件描述符,一个用于读,另一个用于写。都被记录在全局变量中。它实际上是用来发送定时消息的,后面会介绍。
  1. ret = pipe(filedes);  
  2. // ......   
  3. s_fdWakeupRead = filedes[0]; // 消息循环中侦听   
  4. s_fdWakeupWrite = filedes[1]; // 用于通知消息循环定义消息已发送  
        最后一步就是进入消息循环:
  1. // Only returns on error   
  2. ril_event_loop();  
  • ril_event_loop()

        ril_event_loop()中,每次循环都主要做三件事:
        1.初始化。主要是获取要等待的描述符,以及获取超时信息,以便能够及时处理定时消息。
        2.等待。等待socket客户端有新的消息,或一定的时间间隔,之后处理定时消息。
        3.依次处理三种类型的消息。
        下面分别叙述之。先是获取要等待的描述符,这里为什么要这么做呢?我的理解是在添加新的消息时,有可能会修改全局的描述符列表。也就是说,全局的描述符列表有可能在本次循环的处理过程中发生变化。

  1. // make local copy of read fd_set   
  2. memcpy(&rfds, &readFds, sizeof(fd_set));  
        接下来,就是计算select()函数的超时参数。select的最后一个参数代表超时。若为NULL,则select死等;若为0,则select立即返回;若大于0,则限制了select最长的等待时间。
  1. if (-1 == calcNextTimeout(&tv)) { // 获取超时参数,若失败则返回-1。   
  2.     // no pending timers; block indefinitely   
  3.     dlog("~~~~ no timers; blocking indefinitely ~~~~");  
  4.     ptv = NULL;  
  5. else { // 获取成功,则使用该值。   
  6.     dlog("~~~~ blocking for %ds + %dus ~~~~", (int)tv.tv_sec, (int)tv.tv_usec);  
  7.     ptv = &tv;  
  8. }  
        calcNextTimeout()函数先是判断超时消息的列表是否为空,为空则返回-1;
  1. // Sorted list, so calc based on first node   
  2. if (tev == &timer_list) {  
  3.     // no pending timers   
  4.     return -1;  

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多