qcril:第2章 运行状态中的ril_event事件处理机制

第2章 运行状态中的ril_event事件处理机制

 

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

总结:

1FD_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;}

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值