1.1 认识ril_event结构体
ril_event结构体定义在hardware/ril/ril_event.h头文件中。
struct ril_event {
struct ril_event *next; //下一个ril_event;
struct ril_event *prev; //上一个ril_event;
int fd; //文件描述符;
int index; //当前ril事件的索引;
bool persist; //保留当前ril事件标志;
struct timeval timeout; //ril事件超时设置;
ril_event_cb func; //ril事件回调函数的指针。
void *param;}
通过上面的结构体定义,ril_event结构体支持双向链表,每一个节点都有向上一节点和下一节点的指针,并且在此头文件中,定义了针对ril_event双向链表操作的6个函数,分别为:
Ril_event_init:ril_event双向链表初始化操作;
Ril_event_set:设置新创建的ril_event事件参数;
Ril_event_add:增加ril_event事件;
Ril_timer_add:增加ril_event计时器事件;
Ril_event_del:删除ril_event事件;
Ril_event_loop:循环处理ril_event事件。
上面ril_event双向链表的6个函数,处理逻辑都在ril_event.cpp文件中实现。
Ril_event.cpp中实现了ril_event.h头文件中定义的ril_event时间的多个处理函数,其作用是配合ril.cpp完成ril事件的封装和处理。
1.2 RIL事件生命周期控制的处理函数
Ril事件的处理中最重要的就是ril_event_init、ril_event_set、ril_event_add、ril_event_del,这四个函数包括了对ril事件的生命周期的控制。
1.2.1 ril_event_init
在启动LibRIL运行环境的时候,调用eventLoop函数时首先会调用ril_event_init方法,初始化ril_event;它会完成两个ril_event链表的初始化操作:timer_list、pending_list。
Timer_list链表中保存ril的定时事件,pending_list链表中保存ril的请求事件以及初始化ril_event数组watch_table。
图2.1 ril_event_init方法
上面的代码完成4个变量的初始化操作:readFds、timer_list、pending_list和watch_table,后三个与ril_event结构体相关,readFs与fd_set相关。
补充fd_set是什么:
Fd_set是select多端口复用机制中提供的一种数据结构,是long类型数组,其中每一个数组元素都能与其打开的文件描述符关联,从而使用文件描述符进行I/O操作。因此,在处理多个ril_event事件的过程中,会使用到select多端口复用机制。
1.2.2 ril_event_set
在创建ril_event之后,需要调用ril_event_set函数设置其关键的参数,否则ril_event将不能正常工作。
void ril_event_set(struct ril_event * ev, int fd, bool persist, ril_event_cb func, void * param)
memset(ev, 0, sizeof(struct ril_event)); //重置ev;
ev->fd = fd; //设备文件描述符;
ev->index = -1; //设置默认索引为1;
ev->persist = persist; //ril事件持久化标志;
ev->func = func; //事件的callback回调函数;
ev->param = param; //事件参数;
fcntl(fd, F_SETFL, O_NONBLOCK); //设置文件描述符状态,O_NONBLOCK非阻塞I/O。
图2.2 ril_event_set函数
1.2.3 ril_event_add
前面的准备工作好了之后,接着就是ril_event_add函数增加ril_event事件的监听。此方法将前面已经准备好的ril_event保存到watch_table数组中,并根据ril_event->fd文件描述符设置fd_set。
watch_table[i] = ev //将ev保存到watch_table数组中;
ev->index = I //设置索引与数组下标相对应;
FD_SET(ev->fd, &readFds); //设置fd_set,只需监听readFds即可获取对应文件描述符的I/O读写。
if (ev->fd >= nfds) nfds = ev->fd+1;//更新nfds。
图 2.3 ril_event_add函数
在LibRIL运行环境的加载过程中,共创建了3个ril_event结构体,分别是s_wakeupfd_event、s_listen_event、s_debug_event。这三个结构体都保存在watch_table数组中。每个结构体的文件描述符都设置到readFds的fd_set数据类型里,完成了select多端口复用的准备工作。
1.3 ril_event_loop处理机制
LibRIL运行环境的加载过程中,最后会调用ril_event_loop函数,开始监听ril事件;ril_event_loop函数通过for循环select多端口复用,监听设置的前3个文件描述符s_wakeupfd_event->fd、s_list_event->fd和s_debug_event->fd,一旦产生I/O事件,select便会响应,找到对应的ril_event结构体,最后通过结构体的func发起ril事件响应的回调函数的调用。
for (;;) {//一直循环,直到产生异常退出;
memcpy(&rfds, &readFds, sizeof(fd_set)); //复制readFds;
if (-1 == calcNextTimeout(&tv)) {//定时RIL事件的时间更新;
ptv = NULL;
} else {ptv = &tv;}
printReadies(&rfds); //循环watch_table数组,打印fd_set中正常文件描述符
n = select(nfds, &rfds, NULL, NULL, ptv); //多端口复用选择,有I/O事件则返回其文件描述符
printReadies(&rfds);……
processTimeouts();//处理超时的定时RIL事件;
processReadReadies(&rfds, n); //处理select文件描述符RIL事件;
firePending();//使用func进行回调。
图 2.4 ril_event_loop函数
上面的代码,最重要的是processTimeOuts、processReadReadies、firePending这3个函数的调用,可分为两步操作。
1)增加pending_list双向链表中的RIL事件节点,processReadReadies、firePending两个函数都是将对应的RIL时间增加到pending_list双向链表;其中包括了两种类型的RIL事件:已超时的定时RIL事件和接收到的RIL相关请求事件。(pending_list什么时候增加RIL事件的节点的问题答案)
2)调用firePending函数遍历pending_list双向链表获取ril_event,调用其func回调函数,完成对应的RIL事件回调。
1.3.1 增加pending_list双向链表中的RIL事件节点
向pending_list双向链表中增加RIL事件节点的入口有两个地方,processTimeOuts、processReadReadies函数。processTimeOuts函数处理已超时的RIL定时事件,处理逻辑如下:
struct ril_event * tev = timer_list.next; //获取下一个event事件节点;
getNow(&now); //获取当前时间;
//遍历timer_list双向链表,并且判断其中的节点是否有超时的RIl事件;
while ((tev != &timer_list) && (timercmp(&now, &tev->timeout, >))) {
next = tev->next;
removeFromList(tev);
addToList(tev, &pending_list); //增加RIL事件节点到pending_list链表中。
tev = next;
}图2.5 processTimeOuts
上面的代码,两个关键点是:RIL事件超时的时间判断和已经超时的RIL时间增加到pending_list链表中。
1.3.2 processReadReadies处理接收到的RIL请求事件
//循环watch_table数组
for (int i = 0; (i < MAX_FD_EVENTS) && (n > 0); i++) {
//获取watch_table数组中的ril_event;
struct ril_event * rev = watch_table[i];
if (rev != NULL && FD_ISSET(rev->fd, rfds)) {//判断select的事件;
addToList(rev, &pending_list); //增加ril事件节点到pending_list;
if (rev->persist == false) {//判断监听的事件是否持久;
removeWatch(rev, i); //删除监听的事件。}}}
图2.5 processReadReadies
总结:
1)FD_ISSET判断select事件中的文件描述符,根据watch_table数组中对应的文件描述符,找到相应的ril_event事件。
2)调用addToList函数,将ril_event事件增加到pending_list链表中,等待RIL事件的回调。
3)删除非持久的RIL事件。
processTimeOuts函数和processReadReadies函数的处理逻辑,最终将需要触发的RIL的事件增加到pending_list链表中,等待RIL事件的回调。
1.3.3 firePending完成RIL事件的回调
前面已经完成了pending_list链表中需要触发的RIL事件的准备工作,最后是调用firePending函数完成RIL事件的回调。处理逻辑如下:
while (ev != &pending_list) {//遍历pending_list链表;
struct ril_event * next = ev->next;
removeFromList(ev); //删除当前节点;
//callback回调操作,将fd文件描述符作为参数进行回调处理。
ev->func(ev->fd, 0, ev->param);
ev = next;}