电话笔记 | RIL整体框架事件处理介绍

2020-01-03  本文已影响0人  力卉编程

一、 启动事件循环处理eventLoop工作线程

建立多路I/O驱动机制的消息队列,用来接收上层发出的命令以及往Modem发送AT指令的工作,时整个RIL系统的核心部分。创建一个事件分发线程s_tid_dispatch,线程执行体为eventLoop。

图:

eventLoop工作线程时序图

1.1 事件函数入口:eventLoop**

static void * eventLoop(void *param) {  
    int ret;  
    int filedes[2];  
    ril_event_init(); //初始化请求队列  
    pthread_mutex_lock(&s_startupMutex);  
    s_started = 1; //eventLoop线程运行标志位  
    pthread_cond_broadcast(&s_startupCond);  
  pthread_mutex_unlock(&s_startupMutex);  
  //创建匿名管道  
    ret = pipe(filedes);  
    if (ret < 0) {  
        LOGE("Error in pipe() errno:%d", errno);  
        return NULL;  
  }  
  //s_fdWakeupRead为管道读端  
  s_fdWakeupRead = filedes[0];  
  //s_fdWakeupWrite为管道写端  
  s_fdWakeupWrite = filedes[1];  
  //设置管道读端为O_NONBLOCK非阻塞  
  fcntl(s_fdWakeupRead, F_SETFL, O_NONBLOCK);  
  //初始化s_wakeupfd_event结构体的内容,句柄为s_fdWakeupRead,回调函数为   processWakeupCallback  
    ril_event_set (&s_wakeupfd_event, s_fdWakeupRead, true,processWakeupCallback, NULL);  
    ①rilEventAddWakeup (&s_wakeupfd_event);  
    // Only returns on error  
    ②ril_event_loop();  
    LOGE ("error in event_loop_base errno:%d", errno);  
    return NULL;  
}  

1.2 rild两类事件

在rild中定义了event的概念,Rild支持两种类型的事件:

  • 定时事件:根据事件的执行时间来启动执行,通过ril_timer_add添加到time_list队列中
  • Wakeup事件:这些事件的句柄fd将加入的select IO多路复用的句柄池readFDs中,当对应的fd可读时将触发这些事件。对于处于listen端的socket,fd可读表示有个客户端连接,此时需要调用accept接受连接。
struct ril_event {  
    struct ril_event *next;  
    struct ril_event *prev;  
    int fd;  //文件句柄  
    int index; //该事件在监控表中的索引   
    bool persist; //如果是保持的,则不从watch_list 中删除  
    struct timeval timeout; //任务执行时间  
    ril_event_cb func; //回调事件处理函数  
    void *param; //回调时参数  
};  
static struct ril_event s_commands_event;  
ril_event_set (&s_commands_event, s_fdCommand, 1,processCommandsCallback, p_rs)  
  
static struct ril_event s_wakeupfd_event;  
ril_event_set (&s_wakeupfd_event, s_fdWakeupRead, true,processWakeupCallback, NULL)  
  
static struct ril_event s_listen_event;  
ril_event_set (&s_listen_event, s_fdListen, false,listenCallback, NULL)  
  
static struct ril_event s_wake_timeout_event;  
ril_timer_add(&(p_info->event), &myRelativeTime);  
static struct ril_event s_debug_event;  
ril_event_set (&s_debug_event, s_fdDebug, true,debugCallback, NULL)  

1.3 三个事件队列

//事件监控队列
static struct ril_event * watch_table[MAX_FD_EVENTS];
//定时事件队列
static struct ril_event timer_list;
//处理事件队列
static struct ril_event pending_list; //待处理事件队列,事件已经触发,需要所回调处理的事件
1.3.1添加Wakeup 事件
static void rilEventAddWakeup(struct ril_event *ev) {  
    ril_event_add(ev); //向监控表watch_table添加一个s_wakeupfd_event事件  
    triggerEvLoop(); //向管道s_fdWakeupWrite中写入之来触发事件循环  
}  
void ril_event_add(struct ril_event * ev)  
{  
    dlog("~~~~ +ril_event_add ~~~~");  
    MUTEX_ACQUIRE();  
    for (int i = 0; i < MAX_FD_EVENTS; i++) { //遍历监控表watch_table  
        if (watch_table[i] == NULL) { //从监控表中查找空闲的索引,然后把该任务加入到监控表中  
            watch_table[i] = ev; //向监控表中添加事件  
            ev->index = i; //事件的索引设置为在监控表中的索引  
            dlog("~~~~ added at %d ~~~~", i);  
            dump_event(ev);  
            FD_SET(ev->fd, &readFds); //将添加的事件对应的句柄添加到句柄池readFds中  
            if (ev->fd >= nfds) nfds = ev->fd+1; //修改句柄最大值  
            dlog("~~~~ nfds = %d ~~~~", nfds);  
            break;  
        }  
    }  
    MUTEX_RELEASE();  
    dlog("~~~~ -ril_event_add ~~~~");  
}  
1.3.2 触发事件
static void triggerEvLoop() {  
    int ret;  
  if (!pthread_equal(pthread_self(), s_tid_dispatch)) { //如果当前线程ID不等于事件分发线程eventLoop的线程ID  
      do {  
            ret = write (s_fdWakeupWrite, " ", 1); //向管道写端写入值1来触发eventLoop事件循环  
         } while (ret < 0 && errno == EINTR);  
    }  
}  
1.3.3 处理事件
void ril_event_loop()  
{  
    int n;  
    fd_set rfds;  
    struct timeval tv;  
    struct timeval * ptv;  
    for (;;) {  
        memcpy(&rfds, &readFds, sizeof(fd_set));  
        if (-1 == calcNextTimeout(&tv)) {  
            dlog("~~~~ no timers; blocking indefinitely ~~~~");  
            ptv = NULL;  
        } else {  
            dlog("~~~~ blocking for %ds + %dus ~~~~", (int)tv.tv_sec, (int)tv.tv_usec);  
            ptv = &tv;  
        }  
        //使用select 函数等待在FDS 上,只要FDS 中记录的设备有数据到来,select 就会设置相应的标志位并返回。readFDS 记录了所有的事件相关设备句柄。readFDS 中句柄是在在AddEvent 加入的。  
        printReadies(&rfds);  
        n = select(nfds, &rfds, NULL, NULL, ptv);   
        printReadies(&rfds);  
        dlog("~~~~ %d events fired ~~~~", n);  
        if (n < 0) {  
            if (errno == EINTR) continue;  
            LOGE("ril_event: select error (%d)", errno);  
            return;  
        }  
        processTimeouts(); //从timer_list中查询执行时间已到的事件,并添加到pending_list中  
        processReadReadies(&rfds, n); //从watch_table中查询数据可读的事件,并添加到pending_list中去处理,如果该事件不是持久事件,则同时从watch_table中删除  
        //遍历pending_list,调用事件处理回调函数处理所有事件  
        firePending();  
    }  
}
1.3.4 超时事件查询
static void processTimeouts()  
{  
    dlog("~~~~ +processTimeouts ~~~~");  
    MUTEX_ACQUIRE();  
    struct timeval now;  
    struct ril_event * tev = timer_list.next;  
    struct ril_event * next;  
    getNow(&now); //获取当前时间  
  dlog("~~~~ Looking for timers <= %ds + %dus ~~~~", (int)now.tv_sec, (int)now.tv_usec);  
  //如果当前时间大于事件的超时时间,则将该事件从timer_list中移除,添加到pending_list  
    while ((tev != &timer_list) && (timercmp(&now, &tev->timeout, >))) {  
        dlog("~~~~ firing timer ~~~~");  
        next = tev->next;  
        removeFromList(tev); //从timer_list中移除事件  
        addToList(tev, &pending_list); //将事件添加到pending_list  
        tev = next;  
    }  
    MUTEX_RELEASE();  
    dlog("~~~~ -processTimeouts ~~~~");  
}  
1.3.5 可读事件查询
static void processReadReadies(fd_set * rfds, int n)  
{  
    dlog("~~~~ +processReadReadies (%d) ~~~~", n);  
  MUTEX_ACQUIRE();   
  //遍历watch_table数组,根据select返回的句柄n查找对应的事件  
    for (int i = 0; (i < MAX_FD_EVENTS) && (n > 0); i++) {  
        struct ril_event * rev = watch_table[i]; //得到相应的事件  
        if (rev != NULL && FD_ISSET(rev->fd, rfds)) {  
            addToList(rev, &pending_list); //将该事件添加到pending_list中  
            if (rev->persist == false) { //如果该事件不是持久事件还要从watch_table中移除  
                removeWatch(rev, i);  
            }  
            n--;  
        }  
    }  
    MUTEX_RELEASE();  
    dlog("~~~~ -processReadReadies (%d) ~~~~", n);  
}  
1.3.6 事件处理函数firePending
static void firePending()  
{  
    dlog("~~~~ +firePending ~~~~");  
    struct ril_event * ev = pending_list.next;  
    while (ev != &pending_list) { //遍历pending_list链表,处理链表中的所有事件  
        struct ril_event * next = ev->next;  
        removeFromList(ev); //将处理完的事件从pending_list中移除  
        ev->func(ev->fd, 0, ev->param); //调用事件处理的回调函数  
        ev = next;  
    }  
    dlog("~~~~ -firePending ~~~~");  
}  

二、ril事件流程总结

ril事件处理流程

首先通过Linux中的select多路I/O复用对句柄池中的所有句柄进行监控,当有事件到来时select返回,否则阻塞。当select返回时,表示有事件的到来,通过调用processTimeouts函数来处理超时事件,处理方式是遍历time_list链表以查询超时事件,并将超时事件移入到pending_list链表中,接着调用processReadReadies函数来处理触发的事件,处理方式为遍历watch_table列表以查询触发的事件,并将触发的事件移入到pending_list链表中,如果该事件不是持久事件,还需要从watch_table列表中移除,当查询完两种待处理的事件并放入到pending_list链表中后,调用firePending函数对待处理的事件进行集中处理,处理方式为遍历链表,调用每一个事件的回调函数。

完~~

整理 | 力卉编程

上一篇下一篇

猜你喜欢

热点阅读