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_events_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處理。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM