• 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处理。

     

  • 相关阅读:
    动态规划练习 7
    面试经验交流感悟
    反转链表
    动态规划练习 6
    动态规划练习 4
    动态规划练习 9
    动态规划练习 12
    动态规划练习 8
    动态规划练习 13
    动态规划练习 11
  • 原文地址:https://www.cnblogs.com/bastard/p/2755121.html
Copyright © 2020-2023  润新知