一、 启动事件循环处理eventLoop工作线程
建立多路I/O驱动机制的消息队列,用来接收上层发出的命令以及往Modem发送AT指令的工作,时整个RIL系统的核心部分。创建一个事件分发线程s_tid_dispatch,线程执行体为eventLoop。
图:
![](https://img.haomeiwen.com/i19137640/73dfe3f55d37ff68.png)
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事件流程总结
![](https://img.haomeiwen.com/i19137640/5f689f4b425481a5.png)
首先通过Linux中的select多路I/O复用对句柄池中的所有句柄进行监控,当有事件到来时select返回,否则阻塞。当select返回时,表示有事件的到来,通过调用processTimeouts函数来处理超时事件,处理方式是遍历time_list链表以查询超时事件,并将超时事件移入到pending_list链表中,接着调用processReadReadies函数来处理触发的事件,处理方式为遍历watch_table列表以查询触发的事件,并将触发的事件移入到pending_list链表中,如果该事件不是持久事件,还需要从watch_table列表中移除,当查询完两种待处理的事件并放入到pending_list链表中后,调用firePending函数对待处理的事件进行集中处理,处理方式为遍历链表,调用每一个事件的回调函数。
完~~
整理 | 力卉编程
网友评论