客戶端請求處理和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處理。