Ril分析二——事件机制和客户端请求监听
客户端请求处理和Event事件处理机制
一 事件机制
//ril_event.cpp
event事件数据结构:
struct ril_event {
struct ril_event *next;
struct ril_event *prev;
int fd; //事件对应的设备文件句柄
int index;
bool persist;
struct timeval timeout; //事件超时处理时间
ril_event_cb func; //事件处理回调函数
void *param; //回调函数参数
};
相关对象:
fd_set readFds; 所有事件队列中设备文件句柄的集合
int nfds = 0; 所有事件设备句柄中最大值 + 1
ril_event * watch_table[MAX_FD_EVENTS]; 监测事件队列
ril_event timer_list; 时间事件队列
ril_event pending_list; 事件触发 待处理的队列
过程如下:
二 事件机制处理过程
在rild进程的main函数中:
int main(int argc, char **argv) { //创建客户端事件监听线程 RIL_startEventLoop(); //处理客户端请求的模块reference-ril.c funcs_inst[0] = rilInit(&s_rilEnv, argc, s_argv); //注册客户端事件处理接口,并创建socket监听客户端事件 for (i = 0; i < numClients; i++) { RIL_register(funcs_inst[i], i); } }
1 RIL_startEventLoop 创建线程事件循环处理
extern "C" void RIL_startEventLoop(void)
{
//创建eventLoop线程,直到被启动并将s_started置为1返回
s_started = 0;
pthread_mutex_lock(&s_startupMutex); //上锁
pthread_attr_init (&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
ret = pthread_create(&s_tid_dispatch, &attr, eventLoop, NULL);
//线程未启动 休眠等待……
while (s_started == 0) {
pthread_cond_wait(&s_startupCond, &s_startupMutex);
}
pthread_mutex_unlock(&s_startupMutex); //解锁
}
2 RIL_register 注册客户端请求处理接口 创建事件
extern "C" void RIL_register (const
RIL_RadioFunctions *callbacks, int client_id)
{
//注册事件处理接口
memcpy(&s_callbacks[client_id], callbacks, sizeof (RIL_RadioFunctions));
s_registerCalled++;
// Little self-check
//s_commands
//s_unsolResponses
//确保EventLoop启动
if (s_started == 0) {
RIL_startEventLoop();
}
// 创建rild的socket start listen socket
s_fdListen = android_get_control_socket(RIL_getRilSocketName());
ret = listen(s_fdListen, 4);
//创建事件s_listen_event 监听和处理客户端请求
ril_event_set (&s_listen_event, s_fdListen, true,
listenCallback, NULL);
//将s_listen_event事件加入到watch_table队列中 唤醒事件处理线程
rilEventAddWakeup (&s_listen_event);
//建立s_debug_event事件 监听和处理调试
ril_event_set (&s_debug_event, s_fdDebug, true,
debugCallback, NULL);
rilEventAddWakeup (&s_debug_event);
}
创建了s_listen_event s_debug_event事件,加入到watch_table事件列表中,
这些事件将何时,如何被执行呢?
3 EventLoop线程执行函数体
static void * eventLoop(void *param)
{
int ret;
int filedes[2];
//初始化readFds timer_list pending_list watch_table
ril_event_init();
pthread_mutex_lock(&s_startupMutex);
s_started = 1;
pthread_mutex_unlock(&s_startupMutex);
//管道
ret = pipe(filedes);
s_fdWakeupRead = filedes[0];
s_fdWakeupWrite = filedes[1];
fcntl(s_fdWakeupRead, F_SETFL, O_NONBLOCK); //读写操作非阻塞
//建立s_wakeupfd_event事件
ril_event_set (&s_wakeupfd_event, s_fdWakeupRead, true,
processWakeupCallback, NULL);
rilEventAddWakeup (&s_wakeupfd_event);
//进入Event线程循环处理过程
ril_event_loop();
kill(0, SIGKILL);
return NULL;
}
看到这里使用pipe管道s_fdWakeupRead s_fdWakeupWrite是何用意呢?
管道的fd s_fdWakeupRead被加入到readFds集合中;
创建s_wakeupfd_event事件 使用管道fd作为其设备文件描述符
4 Event线程 ril_event_loop循环处理过程
void ril_event_loop()
{
for (;;) {
// make local copy of read fd_set
memcpy(&rfds, &readFds, sizeof(fd_set));
//计算
calcNextTimeout(&tv);
//从设备节点中读取有变化的值并返回
n = select(nfds, &rfds, NULL, NULL, ptv);
//处理timer_list中事件 将过时事件移到pending_list中
processTimeouts();
//处理watch_table中事件 将其移到pending_list中
processReadReadies(&rfds, n);
//处理pending_list中事件
firePending();
}
}
事件处理:
static void firePending()
{
struct ril_event * ev = pending_list.next;
while (ev != &pending_list) {
struct ril_event * next = ev->next;
removeFromList(ev);
//执行事件处理函数
ev->func(ev->fd, 0, ev->param);
ev = next;
}
}
这就是整个Event处理的大致流程。这里处理的事件不是外部而是来自内部自定义的事件。
5 pipe 与 select rilEventAddWakeup
这里需要关注的几个地方是:
pipe管道的使用
rilEventAddWakeup 如何唤醒线程
ril_event_loop 中select调用
这之间是什么关系呢?
定义了管道:
s_fdWakeupRead = filedes[0];
s_fdWakeupWrite = filedes[1];
rilEventAddWakeup函数:
static void rilEventAddWakeup(struct ril_event *ev) {
ril_event_add(ev);
triggerEvLoop();
}
ril_event_add函数:
void ril_event_add(struct ril_event * ev)
{
for (int i = 0; i < MAX_FD_EVENTS; i++) {
if (watch_table[i] == NULL) {
watch_table[i] = ev;
ev->index = i;
//将当前事件的设备节点文件描述符加入到集合readFds
FD_SET(ev->fd, &readFds);
if (ev->fd >= nfds) nfds = ev->fd+1;
break;
}
}
}
readFds定义:static fd_set readFds;
struct fd_set一个存放文件描述符(file descriptor),即文件句柄的聚合管道文件句柄就是被加入到此集合当中。
这个文件句柄集合 就是用selecet函数进行监听,一旦文件句柄任一有变化,select就会读取相关变化的值,并返回。
select可以阻塞也可以是非阻塞,根据传递参数而定。
triggerEvLoop函数:
static void triggerEvLoop() {
int ret;
if (!pthread_equal(pthread_self(), s_tid_dispatch)) {
do {
//若当前的执行进程不是s_tid_dispatch
//则向管道中写入数据,使readFds集合中文件描述符有变化 以触发线程
ret = write (s_fdWakeupWrite, " ", 1);
} while (ret < 0 && errno == EINTR);
}
}
ril_event_loop函数:
void ril_event_loop()
{
for (;;) {
n = select(nfds, &rfds, NULL, NULL, ptv);
……
}
}
select函数监听文件句柄集合,当句柄中任何一个数据变化时,就会读取它并返回。
否则为定时情况有可能会一直处于阻塞之中,事件得不到执行。
事件调度处理过程如上所述,事件的作用如何呢,通过这些事件做了些什么事情,客户端的请求是如何处理的,这些并不清楚。
三 事件处理
从前面可以看到创建了事件,事件在被移动到待执行列表pending_list后,将在线程循环结构中得到处理,
调用事件的回调处理函数,有如下事件:
ril_event s_wakeupfd_event; //清空用于唤醒管道数据
ril_event s_listen_event; //接收客户端socket请求
ril_event s_debug_event; //用于调试
ril_event s_commands_event; //执行客户端请求
s_listen_event:监听rild端口的客户端连接,如果监听到有新的连接就会执行,使用fd = accept(s_fdListen,…);
得到一个新的套接字文件描述符,用来和建立连接的客户端进行通信:接收和发送消息。
只有新客户端与Rild Sokcet建立连接时,才会触发s_listen_event事件。
s_commands_event:s_listen_event监听到客户端连接,使用accept并建立新的通信套接字fd后,
使用新建立的fd创建s_commands_event事件,从客户端接收消息和向客户端发送消息;
1 s_wakeupfd_event
processWakeupCallback:
static void processWakeupCallback(int fd, short flags, void *param) {
/* empty our wakeup socket out */
do {
ret = read(s_fdWakeupRead, &buff, sizeof(buff));
} while (ret > 0 || (ret < 0 && errno == EINTR));
}
Pipe管道的特性,分为读取端和写入端,读取之后会将其中所有数据清空。
2 s_listen_event
listenCallback:
static void listenCallback (int fd, short flags, void *param) {
int is_phone_socket = 0;
char *p_record;
RecordStreamInfo *p_rsInfo;
struct passwd *pwd = NULL;
//监听socket连接
fd = accept(s_fdListen, (sockaddr *) &peeraddr, &socklen);
//判断是否是PHONE_PROCESS 连接 否则返回
err = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &creds, &szCreds);
pwd = getpwuid(creds.uid);
if (strcmp(pwd->pw_name, PHONE_PROCESS) == 0) {
is_phone_socket = 1;
}
// 创建RecordStream数据结构record_stream.c
p_rsInfo = new RecordStreamInfo();
p_rsInfo->p_rs = record_stream_new(fd, MAX_COMMAND_BYTES);
p_record = (char *)malloc(sizeof(char) * SUB_DATA_LENGTH);
//从socket中读取数据
ret = read(fd, p_record, SUB_DATA_LENGTH);
s_fdCommand[p_rsInfo->client_id] = fd;
//创建事件s_commands_event 处理socket请求
ril_event_set (&s_commands_event[client_id], s_fdCommand[client_id], 1,
processCommandsCallback, p_rsInfo);
rilEventAddWakeup (&s_commands_event[p_rsInfo->client_id]);
//通知客户端已建立连接
onNewCommandConnect(p_rsInfo->client_id);
}
s_listen_event事件监听客户端连接,创建s_commands_event事件来处理请求。
3 s_commands_event processCommandsCallback:
static void processCommandsCallback(int fd, short flags, void *param) {
void *p_record;
RecordStreamInfo *p_rsInfo;
p_rsInfo = (RecordStreamInfo *)param;
for (;;) {
/* loop until EAGAIN/EINTR, end of stream, or other error */
ret = record_stream_get_next(p_rsInfo->p_rs, &p_record, &recordlen);
if (ret == 0 && p_record == NULL) {
/* end-of-stream */
break;
} else if (ret < 0) {
break;
} else if (ret == 0) { /* && p_record != NULL */
processCommandBuffer(p_record, recordlen, p_rsInfo->client_id);
}
}
}
static int processCommandBuffer(void *buffer, size_t buflen, int client_id) {
Parcel p;
status_t status;
int32_t request;
int32_t token;
RequestInfo *pRI;
//将数据转化成Parcel类型
p.setData((uint8_t *) buffer, buflen);
// 读取请求的标识
status = p.readInt32(&request);
status = p.readInt32 (&token);
//将数据请求转化成RequestInfo类型
pRI = (RequestInfo *)calloc(1, sizeof(RequestInfo));
//查询获取客户端请求处理接口 CommandInfo
pRI->pCI = &(s_commands[request]);
pRI->token = token;
pRI->client_id = client_id;
pRI->p_next = s_pendingRequests;
s_pendingRequests = pRI;
//调用请求对应的处理函数
pRI->pCI->dispatchFunction(p, pRI);
return 0;
}
客户端传来的请求都对应着相应的请求标识号,通过该标识号查询每一个客户端请求RequestInfo对应着 处理和响应接口信息CommandInfo
查询是通过s_commands数组进行的。
四 客户端请求处理接口
将客户端通过socket发来请求转化成RequestInfo进行处理,然后查询对应请求的处理接口CommandInfo。
typedef struct {
int requestNumber;
void (*dispatchFunction) (Parcel &p, struct RequestInfo *pRI);
int(*responseFunction) (Parcel &p, void *response, size_t responselen);
} CommandInfo;
static CommandInfo s_commands[] = {
#include "ril_commands.h"
};
ril_commands.h:
{RIL_REQUEST_GET_SIM_STATUS, dispatchVoid, responseSimStatus},
{RIL_REQUEST_ENTER_SIM_PIN, dispatchStrings, responseInts},
{RIL_REQUEST_ENTER_SIM_PUK, dispatchStrings, responseInts},
{RIL_REQUEST_ENTER_SIM_PIN2, dispatchStrings, responseInts},
{RIL_REQUEST_ENTER_SIM_PUK2, dispatchStrings, responseInts},
……
调用其处理函数:RequestInfo-> CommandInfo ->dispatchFunction(p, pRI);
dispatchFunction函数中调用s_callbacks中的请求处理函数onRequest
s_callbacks就是在rild的main函数中RIL_Init初始化(reference-ril.c),是所返回的接口,注册到EventLoop(Ril.cpp)中.
static const RIL_RadioFunctions s_callbacks = {
onRequest,
……
};
dispatchFunction——>onRequest:将客户端请求派发给reference-ril处理。