第一个关键点是RIL_startEventLoop函数,这个函数实际上是由libRil.so实现的,它的代码在Ril.cpp中,代码如下所示:
**Ril.cpp**
~~~
extern "C" void RIL_startEventLoop(void){
intret;
pthread_attr_t attr;
s_started= 0;
pthread_mutex_lock(&s_startupMutex);
pthread_attr_init (&attr);
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
//创建工作线程eventLoop
ret =pthread_create(&s_tid_dispatch, &attr, eventLoop, NULL);
/*
工作线程eventLoop运行后会设置s_started为1,并触发s_startupCond。
这几个语句的目的是保证在RIL_startEventLoop返回前,工作线程一定是已经创建并运行了
*/
while(s_started == 0) {
pthread_cond_wait(&s_startupCond, &s_startupMutex);
}
pthread_mutex_unlock(&s_startupMutex);
if(ret < 0) {
return;
}
}
~~~
从上面代码中可知,RIL_startEventLoop会等待工作线程创建并运行成功。这个线程为什么会如此重要呢?下面就来了解一下工作线程eventLoop。
1. 工作线程eventLoop
工作线程eventLoop的代码如下所示:
**Ril.cpp**
~~~
static void * eventLoop(void *param) {
intret;
intfiledes[2];
//①初始化请求队列
ril_event_init();
//下面这几个操作告诉RIL_startEventLoop函数本线程已经创建并成功运行了。
pthread_mutex_lock(&s_startupMutex);
s_started = 1;
pthread_cond_broadcast(&s_startupCond);
pthread_mutex_unlock(&s_startupMutex);
//创建匿名管道
ret =pipe(filedes);
......
s_fdWakeupRead = filedes[0];
s_fdWakeupWrite = filedes[1];
//设置管道读端口的属性为非阻塞
fcntl(s_fdWakeupRead, F_SETFL, O_NONBLOCK);
//②下面这两句话将匿名管道的读写端口加入到event队列中。
ril_event_set (&s_wakeupfd_event, s_fdWakeupRead, true,
processWakeupCallback, NULL);
rilEventAddWakeup (&s_wakeupfd_event);
//③进入事件等待循环中,等待外界触发事件并做对应的处理。
ril_event_loop();
returnNULL;
}
~~~
工作线程的工作并不复杂,主要有三个关键点。
(1)ril_event_init的分析
工作线程,顾名思义就是用来干活的。要让它干活,是否得有一些具体的任务呢?它是如何管理这些任务的呢?对这两问题的回答是:
- 工作线程使用了一个叫ril_event的结构体,来描述一个任务,并且它将多个任务按时间顺序组织起来,保存在任务队列中。这个时间顺序是指该任务的执行时间,由外界设定,可以是未来的某时间。
r
il_event_init函数就是用来初始化相关队列和管理结构的,代码如下所示:
在代码中,“任务”也称为“事件”,如没有特殊说明必要,这两者以后不再做区分。
**Ril.cpp**
~~~
void ril_event_init()
{
MUTEX_INIT();//初始化一个mutex对象listMutex
FD_ZERO(&readFds);//初始化readFds,看来Ril会使用select来做多路IO复用
//下面的timer_list和pending_list分别是两个队列
init_list(&timer_list);//初始化timer_list,任务插入的时候按时间排序
init_list(&pending_list);//初始化pendling_list,保存每次需要执行的任务
/*
watch_table(监控表)定义如下:
static struct ril_event * watch_table[MAX_FD_EVENTS];
其中MAX_FD_EVENTS的值为8。监控表主要用来保存那些FD已经加入到readFDs中的
任务。
*/
memset(watch_table, 0, sizeof(watch_table));
}
~~~
此ril_event_init函数没什么新鲜的内容。任务在代码中的对等物Ril_event结构的代码,如下所示:
**Ril_event.h**
~~~
struct ril_event {
structril_event *next;
structril_event *prev;//next和prev将ril_event组织成了一个双向链表
intfd; //该任务对应的文件描述符,以后简称FD。
intindex; //这个任务在监控表中的索引
/*
是否永久保存在监控表中,一个任务处理完毕后将根据这个persist参数来判断
是否需要从监控表中移除。
*/
boolpersist;
structtimeval timeout; //该任务的执行时间
ril_event_cb func; //任务函数
void*param; //传给任务函数的参数
};
~~~
ril_event_init刚初始化完任务队列,下面就有地方添加任务了。
(2)任务加入队列
下面这两行代码初始化一个FD为s_wakeupfd_event的任务,并将其加入到监控表中:
~~~
/*
s_wakeupfd_event定义为一个静态的ril_event,ril_event_set函数将初始化它的
FD为管道的读端,任务函数ril_event_cb对应为processWakeupCallback,
并设置persist为true
*/
ril_event_set (&s_wakeupfd_event, s_fdWakeupRead,true,
processWakeupCallback, NULL);
//来看这个函数:
rilEventAddWakeup (&s_wakeupfd_event);
~~~
rilEventAddWakeup比较有意思,来看这个函数;
**Ril.cpp**
~~~
static void rilEventAddWakeup(struct ril_event*ev) {
ril_event_add(ev);//ev指向一条任务
triggerEvLoop();
}
//直接看ril_event_add函数和triggerEvLoop函数。
void ril_event_add(struct ril_event * ev)
{
......
MUTEX_ACQUIRE();//锁保护
for (int i =0; i < MAX_FD_EVENTS; i++) {
//从监控表中找到第一个空闲的索引,然后把这个任务加到监控表中,
//index表示这个任务在监控中的索引
if(watch_table[i] == NULL) {
watch_table[i] = ev;
ev->index = i;
......
//将任务的FD加入到readFds中,这是select使用的标准方法
FD_SET(ev->fd, &readFds);
if (ev->fd >= nfds) nfds = ev->fd+1;
......
break;
}
}
MUTEX_RELEASE();
......
}
//再来看triggerEvLoop函数,这个更简单了:
static void triggerEvLoop() {
intret;
/*
s_tid_dispatch是工作线程eventLoop的线程ID,pthread_self返回调用线程的线程ID。
由于这里调用triggerEvLoop的就是eventLoop自己,所以不会走if 分支。但是可以看看
里面的内容。
*/
if(!pthread_equal(pthread_self(), s_tid_dispatch)) {
do{
//s_fdWakeupWrite为匿名管道的写端口,看来触发eventLoop工作的条件就是
//往这个端口写一点数据了。
ret = write (s_fdWakeupWrite, " ", 1);
}while (ret < 0 && errno == EINTR);
}
}
~~~
一般的线程间通信使用同步对象来触发,而rild是通过往匿名管道写数据来触发工作线程工作的。
(3)ril_event_loop的分析
来看最后一个关键函数ril_event_loop,其代码如下所示:
**Ril.cpp**
~~~
void ril_event_loop()
{
int n;
fd_setrfds;
structtimeval tv;
structtimeval * ptv;
for(;;) {
memcpy(&rfds, &readFds,sizeof(fd_set));
/*
根据timer_list来计算select函数的等待时间,timer_list已经
按任务的执行时间排好序了。
*/
if(-1 == calcNextTimeout(&tv)) {
ptv = NULL;
}else {
ptv = &tv;
}
......;
//调用select进行多路IO复用
n= select(nfds, &rfds, NULL, NULL, ptv);
......
//将timer_list中那些执行时间已到的任务移到pending_list队列。
processTimeouts();
//从监控表中转移那些有数据要读的任务到pending_list队列,如果任务的persisit不为
//true,则同时从监控表中移除这些任务
processReadReadies(&rfds, n);
//遍历pending_list,执行任务的任务函数。
firePending();
}
}
~~~
根据对ril_event_Loop函数的分析可知,Rild支持两种类型的任务:
- 定时任务。它的执行由执行时间决定,和监控表没有关系,在Ril.cpp中由ril_timer_add函数添加。
- 非定时任务,也叫Wakeup Event。这些任务的FD将加入到select的读集合(readFDs)中,并且在监控表中存放了对应的任务信息。它们触发的条件是这些FD可读。对于管道和Socket来说,FD可读意味着接收缓冲区中有数据,这时调用recv不会因为没有数据而阻塞。
对于处于listen端的socket来说,FD可读表示有客户端连接上了,此时需要调用accept接受连接。
2. RIL_startEventLoop小结
总结一下RIL_startEventLoop的工作。从代码中看,这个函数将启动一个比较重要的工作线程eventLoop,该线程主要用来完成一些任务处理,而目前还没有给它添加任务。
- 前言
- 第1章 阅读前的准备工作
- 1.1 系统架构
- 1.1.1 Android系统架构
- 1.1.2 本书的架构
- 1.2 搭建开发环境
- 1.2.1 下载源码
- 1.2.2 编译源码
- 1.3 工具介绍
- 1.3.1 Source Insight介绍
- 1.3.2 Busybox的使用
- 1.4 本章小结
- 第2章 深入理解JNI
- 2.1 JNI概述
- 2.2 学习JNI的实例:MediaScanner
- 2.3 Java层的MediaScanner分析
- 2.3.1 加载JNI库
- 2.3.2 Java的native函数和总结
- 2.4 JNI层MediaScanner的分析
- 2.4.1 注册JNI函数
- 2.4.2 数据类型转换
- 2.4.3 JNIEnv介绍
- 2.4.4 通过JNIEnv操作jobject
- 2.4.5 jstring介绍
- 2.4.6 JNI类型签名介绍
- 2.4.7 垃圾回收
- 2.4.8 JNI中的异常处理
- 2.5 本章小结
- 第3章 深入理解init
- 3.1 概述
- 3.2 init分析
- 3.2.1 解析配置文件
- 3.2.2 解析service
- 3.2.3 init控制service
- 3.2.4 属性服务
- 3.3 本章小结
- 第4章 深入理解zygote
- 4.1 概述
- 4.2 zygote分析
- 4.2.1 AppRuntime分析
- 4.2.2 Welcome to Java World
- 4.2.3 关于zygote的总结
- 4.3 SystemServer分析
- 4.3.1 SystemServer的诞生
- 4.3.2 SystemServer的重要使命
- 4.3.3 关于 SystemServer的总结
- 4.4 zygote的分裂
- 4.4.1 ActivityManagerService发送请求
- 4.4.2 有求必应之响应请求
- 4.4.3 关于zygote分裂的总结
- 4.5 拓展思考
- 4.5.1 虚拟机heapsize的限制
- 4.5.2 开机速度优化
- 4.5.3 Watchdog分析
- 4.6 本章小结
- 第5章 深入理解常见类
- 5.1 概述
- 5.2 以“三板斧”揭秘RefBase、sp和wp
- 5.2.1 第一板斧--初识影子对象
- 5.2.2 第二板斧--由弱生强
- 5.2.3 第三板斧--破解生死魔咒
- 5.2.4 轻量级的引用计数控制类LightRefBase
- 5.2.5 题外话-三板斧的来历
- 5.3 Thread类及常用同步类分析
- 5.3.1 一个变量引发的思考
- 5.3.2 常用同步类
- 5.4 Looper和Handler类分析
- 5.4.1 Looper类分析
- 5.4.2 Handler分析
- 5.4.3 Looper和Handler的同步关系
- 5.4.4 HandlerThread介绍
- 5.5 本章小结
- 第6章 深入理解Binder
- 6.1 概述
- 6.2 庖丁解MediaServer
- 6.2.1 MediaServer的入口函数
- 6.2.2 独一无二的ProcessState
- 6.2.3 时空穿越魔术-defaultServiceManager
- 6.2.4 注册MediaPlayerService
- 6.2.5 秋风扫落叶-StartThread Pool和join Thread Pool分析
- 6.2.6 你彻底明白了吗
- 6.3 服务总管ServiceManager
- 6.3.1 ServiceManager的原理
- 6.3.2 服务的注册
- 6.3.3 ServiceManager存在的意义
- 6.4 MediaPlayerService和它的Client
- 6.4.1 查询ServiceManager
- 6.4.2 子承父业
- 6.5 拓展思考
- 6.5.1 Binder和线程的关系
- 6.5.2 有人情味的讣告
- 6.5.3 匿名Service
- 6.6 学以致用
- 6.6.1 纯Native的Service
- 6.6.2 扶得起的“阿斗”(aidl)
- 6.7 本章小结
- 第7章 深入理解Audio系统
- 7.1 概述
- 7.2 AudioTrack的破解
- 7.2.1 用例介绍
- 7.2.2 AudioTrack(Java空间)分析
- 7.2.3 AudioTrack(Native空间)分析
- 7.2.4 关于AudioTrack的总结
- 7.3 AudioFlinger的破解
- 7.3.1 AudioFlinger的诞生
- 7.3.2 通过流程分析AudioFlinger
- 7.3.3 audio_track_cblk_t分析
- 7.3.4 关于AudioFlinger的总结
- 7.4 AudioPolicyService的破解
- 7.4.1 AudioPolicyService的创建
- 7.4.2 重回AudioTrack
- 7.4.3 声音路由切换实例分析
- 7.4.4 关于AudioPolicy的总结
- 7.5 拓展思考
- 7.5.1 DuplicatingThread破解
- 7.5.2 题外话
- 7.6 本章小结
- 第8章 深入理解Surface系统
- 8.1 概述
- 8.2 一个Activity的显示
- 8.2.1 Activity的创建
- 8.2.2 Activity的UI绘制
- 8.2.3 关于Activity的总结
- 8.3 初识Surface
- 8.3.1 和Surface有关的流程总结
- 8.3.2 Surface之乾坤大挪移
- 8.3.3 乾坤大挪移的JNI层分析
- 8.3.4 Surface和画图
- 8.3.5 初识Surface小结
- 8.4 深入分析Surface
- 8.4.1 与Surface相关的基础知识介绍
- 8.4.2 SurfaceComposerClient分析
- 8.4.3 SurfaceControl分析
- 8.4.4 writeToParcel和Surface对象的创建
- 8.4.5 lockCanvas和unlockCanvasAndPost分析
- 8.4.6 GraphicBuffer介绍
- 8.4.7 深入分析Surface的总结
- 8.5 SurfaceFlinger分析
- 8.5.1 SurfaceFlinger的诞生
- 8.5.2 SF工作线程分析
- 8.5.3 Transaction分析
- 8.5.4 关于SurfaceFlinger的总结
- 8.6 拓展思考
- 8.6.1 Surface系统的CB对象分析
- 8.6.2 ViewRoot的你问我答
- 8.6.3 LayerBuffer分析
- 8.7 本章小结
- 第9章 深入理解Vold和Rild
- 9.1 概述
- 9.2 Vold的原理与机制分析
- 9.2.1 Netlink和Uevent介绍
- 9.2.2 初识Vold
- 9.2.3 NetlinkManager模块分析
- 9.2.4 VolumeManager模块分析
- 9.2.5 CommandListener模块分析
- 9.2.6 Vold实例分析
- 9.2.7 关于Vold的总结
- 9.3 Rild的原理与机制分析
- 9.3.1 初识Rild
- 9.3.2 RIL_startEventLoop分析
- 9.3.3 RIL_Init分析
- 9.3.4 RIL_register分析
- 9.3.5 关于Rild main函数的总结
- 9.3.6 Rild实例分析
- 9.3.7 关于Rild的总结
- 9.4 拓展思考
- 9.4.1 嵌入式系统的存储知识介绍
- 9.4.2 Rild和Phone的改进探讨
- 9.5 本章小结
- 第10章 深入理解MediaScanner
- 10.1 概述
- 10.2 android.process.media分析
- 10.2.1 MSR模块分析
- 10.2.2 MSS模块分析
- 10.2.3 android.process.media媒体扫描工作的流程总结
- 10.3 MediaScanner分析
- 10.3.1 Java层分析
- 10.3.2 JNI层分析
- 10.3.3 PVMediaScanner分析
- 10.3.4 关于MediaScanner的总结
- 10.4 拓展思考
- 10.4.1 MediaScannerConnection介绍
- 10.4.2 我问你答
- 10.5 本章小结