chromium IPC msg发送接收

https://chromium.googlesource.com/chromium/src/+/master/mojo/README.md#System-Overview   

之前看博客关注比较多的都是render、browser或者是资源下载,IPC会关注的比较少,后面发现这个模块还是建议梳理下,便于对整个流程的理解。IPC 就是进程间通信,如何发送消息,发送的消息格式是怎样的,如何接收消息。 如果能静心看浏览器代码,会有很多意外收获的,虽然浏览的代码很多很大。下面我们就以URL加载过程进行介绍。

    之前URL加载流程中,从JNI函数中获取URL地址,调用LoadURLWithParams传递URL等设置参数,最终会调用到Navigate函数,这整个流程的调用都在CrBrowserMain中。在Navigate中会调用一个SendNavigateMessage函数。

SendNavigateMessage函数的实现如下,看代码,其实功能很简单就调用一个Send函数发送一个MSG。

void RenderFrameHostImpl::SendNavigateMessage(
    const CommonNavigationParams& common_params,
    const StartNavigationParams& start_params,
    const RequestNavigationParams& request_params) {  
  RenderFrameDevToolsAgentHost::OnBeforeNavigation(
      frame_tree_node_->current_frame_host(), this);
  Send(new FrameMsg_Navigate(
      routing_id_, common_params, start_params, request_params));
}

代码路径:  ./content/browser/frame_host/render_frame_host_impl.cc

我们来看看Send 函数的实现,send函数很简单,先调用ipc定义的IPC_MESSAGE_ID_CLASS将获取的type右移16位,同IPCMessageStart枚举中的变量InputMsgStart(4)进行比较,然后将消息发送出去。再往回去看,发送消息是send(msg),这个msg 都是在发送之前新建的。之前一直没找到FrameMsg_Navigate的定义,后面找到相关的是如下代码:

bool RenderFrameHostImpl::Send(IPC::Message* message) {
  if (IPC_MESSAGE_ID_CLASS(message->type()) == InputMsgStart) {
    return GetRenderWidgetHost()->input_router()->SendInput(
        base::WrapUnique(message));
  }

  return GetProcess()->Send(message);
}

代码路径:./content/browser/frame_host/render_frame_host_impl.cc  

IPC_MESSAGE_ROUTED3(FrameMsg_Navigate,
                    content::CommonNavigationParams, /* common_params */
                    content::StartNavigationParams,  /* start_params */
                    content::RequestNavigationParams /* request_params */)

代码路径:./content/common/frame_messages.h

可以从IPC模块中找到IPC_MESSAGE_ROUNTED3的定义,原来大神们通过一串的define将两类消息(CONTROL、ROUTED)定义成相关的数据结构,也就是msg的格式。

#define IPC_MESSAGE_ROUTED0(msg) IPC_MESSAGE_ROUTED(msg)
#define IPC_MESSAGE_ROUTED1(msg, a) IPC_MESSAGE_ROUTED(msg, a)
#define IPC_MESSAGE_ROUTED2(msg, a, b) IPC_MESSAGE_ROUTED(msg, a, b)
#define IPC_MESSAGE_ROUTED3(msg, a, b, c) IPC_MESSAGE_ROUTED(msg, a, b, c)
#define IPC_MESSAGE_ROUTED4(msg, a, b, c, d) IPC_MESSAGE_ROUTED(msg, a, b, c, d)
#define IPC_MESSAGE_ROUTED5(msg, a, b, c, d, e) \
  IPC_MESSAGE_ROUTED(msg, a, b, c, d, e)
#define IPC_MESSAGE_CONTROL(msg_class, ...) \
  IPC_MESSAGE_DECL(msg_class, CONTROL, IPC_TUPLE(__VA_ARGS__), void)
#define IPC_MESSAGE_ROUTED(msg_class, ...) \
  IPC_MESSAGE_DECL(msg_class, ROUTED, IPC_TUPLE(__VA_ARGS__), void)
#define IPC_MESSAGE_DECL(msg_name, kind, in_tuple, out_tuple)       \
  struct IPC_MESSAGE_EXPORT msg_name##_Meta {                       \
    using InTuple = in_tuple;                                       \
    using OutTuple = out_tuple;                                     \
    enum { ID = IPC_MESSAGE_ID() };                                 \
    static const IPC::MessageKind kKind = IPC::MessageKind::kind;   \
    static const char kName[];                                      \
  };                                                                \
  extern template class EXPORT_TEMPLATE_DECLARE(IPC_MESSAGE_EXPORT) \
      IPC::MessageT;                               \
  using msg_name = IPC::MessageT;                  \
  IPC_MESSAGE_EXTRA(msg_name)

代码路径:./ipc/ipc_message_macros.h

看到这消息格式应该算是明朗了,接下来分析的是如何进行发送消息。

然后我们来看看msg的接收,先看OnMessageReceived的实现

bool RenderProcessHostImpl::OnMessageReceived(const IPC::Message& msg) {
......
  if (msg.routing_id() == MSG_ROUTING_CONTROL) {
    // Dispatch control messages.
    IPC_BEGIN_MESSAGE_MAP(RenderProcessHostImpl, msg)
      IPC_MESSAGE_HANDLER(ChildProcessHostMsg_ShutdownRequest,
                          OnShutdownRequest)
      IPC_MESSAGE_HANDLER(RenderProcessHostMsg_SuddenTerminationChanged,
                          SuddenTerminationChanged)
      IPC_MESSAGE_HANDLER(ViewHostMsg_UserMetricsRecordAction,
                          OnUserMetricsRecordAction)
      IPC_MESSAGE_HANDLER(ViewHostMsg_Close_ACK, OnCloseACK)
#if defined(ENABLE_WEBRTC)
      IPC_MESSAGE_HANDLER(AecDumpMsg_RegisterAecDumpConsumer,
                          OnRegisterAecDumpConsumer)
      IPC_MESSAGE_HANDLER(AecDumpMsg_UnregisterAecDumpConsumer,
                          OnUnregisterAecDumpConsumer)
#endif
    // Adding single handlers for your service here is fine, but once your
    // service needs more than one handler, please extract them into a new
    // message filter and add that filter to CreateMessageFilters().
    IPC_END_MESSAGE_MAP()

    return true;
  }

    代码路径:./content/browser/renderer_host/render_process_host_impl.cc

  看上面的代码,还是要在IPC模块中先看IPC_BEGIN_MESSAGE_MAP、IPC_MESSAGE_HANDLER、  IPC_END_MESSAGE_MAP的定义。IPC_BEGIN_MESSAGE_MAP是一个switch开始,IPC_MESSAGE_HANDLER是一个case语句dispatch message,其中的一个入参是函数,作为回调函数,即在dispatch message的时候就调用相关的函数,IPC_END_MESSAGE_MAP就只是括号。看到这,我算是长见识了,原来代码是可以怎么写的。

#define IPC_BEGIN_MESSAGE_MAP(class_name, msg) \
  { \
    typedef class_name _IpcMessageHandlerClass ALLOW_UNUSED_TYPE; \
    void* param__ = NULL; \
    (void)param__; \
    const IPC::Message& ipc_message__ = msg; \
    switch (ipc_message__.type()) {
    
#define IPC_MESSAGE_FORWARD(msg_class, obj, member_func)                       \
    case msg_class::ID: {                                                      \
        TRACK_RUN_IN_THIS_SCOPED_REGION(member_func);                          \
        if (!msg_class::Dispatch(&ipc_message__, obj, this, param__,           \
                                 &member_func))                                \
          ipc_message__.set_dispatch_error();                                  \
      }                                                                        \
      break;

#define IPC_MESSAGE_HANDLER(msg_class, member_func) \
  IPC_MESSAGE_FORWARD(msg_class, this, _IpcMessageHandlerClass::member_func)

  代码路径:./ipc/ipc_message_macros.h

#define IPC_END_MESSAGE_MAP() \
  } \
}

    代码路径:./ipc/ipc_message_macros.h

将class 入参传入,函数作为回调函数,所以会有在对应的class Dispatch msg时,调用相关的函数。

 

 

你可能感兴趣的:(chromium)