chromium通信系统-ipcz系统(十一)-mojo binding

关于mojo binding的官方文档为mojo docs。 由于比较复杂,这里只做简单源码分析。

我们知道要实现rpc,必须实现客户端和服务端。 mojo 实现了一套领域语言,通过领域语言描述接口和数据, 再通过特有编译器编译成c++代码。 这个过程会生成Mojo对象, 我们以content/common/child_process.mojom 为例子来分析。

cpp 复制代码
interface ChildProcess {
 ......

  // Requests that the process bind a receiving pipe targeting the service
  // interface named by |receiver|.
  //
  // TODO(crbug.com/977637): Rename this to |RunService()| once the above method
  // is removed.
  BindServiceInterface(mojo_base.mojom.GenericPendingReceiver receiver);

  // Requests that the process bind a receiving pipe targeting the interface
  // named by |receiver|. Unlike |BindServiceInterface()| this may be used to
  // bind arbitrary interfaces on many different types of child processes.
  // Calls to this method generally end up in
  // |ChildThreadImpl::OnBindReceiver()|.
  //
  // Whether or not the interface type encapsulated by |receiver| is supported
  // depends on the process type and potentially on the Content embedder.
  BindReceiver(mojo_base.mojom.GenericPendingReceiver receiver);
  ......
};

我们删除一些接口,总体定义如下。生成带c++代码如下。

out/Default/gen/content/common/child_process.mojom.h

cpp 复制代码
class CONTENT_EXPORT ChildProcess
    : public ChildProcessInterfaceBase {
 public:
  ......
  // 接口的名字
  static const char Name_[];
  // 方法表,用于将消息序号转化为处理函数
  static IPCStableHashFunction MessageToMethodInfo_(mojo::Message& message);
  // 方法名称表
  static const char* MessageToMethodName_(mojo::Message& message);
  // 协议版本
  static constexpr uint32_t Version_ = 0;
  static constexpr bool PassesAssociatedKinds_ = false;
  // 是否包含不可中断的方法
  static constexpr bool HasUninterruptableMethods_ = false;

  using Base_ = ChildProcessInterfaceBase;
  // 代理对象,用于客户端调用
  using Proxy_ = ChildProcessProxy;

  template <typename ImplRefTraits>
  // 用于指向服务端实现
  using Stub_ = ChildProcessStub<ImplRefTraits>;
  
  // 请求校验器
  using RequestValidator_ = ChildProcessRequestValidator;
  // 回复校验器
  using ResponseValidator_ = mojo::PassThroughFilter;
  // 方法版本列表
  enum MethodMinVersions : uint32_t {
    kProcessShutdownMinVersion = 0,
    kSetIPCLoggingEnabledMinVersion = 0,
    kGetBackgroundTracingAgentProviderMinVersion = 0,
    kEnableSystemTracingServiceMinVersion = 0,
    kCrashHungProcessMinVersion = 0,
    kRunServiceDeprecatedMinVersion = 0,
    kBindServiceInterfaceMinVersion = 0,
    kBindReceiverMinVersion = 0,
    kSetPseudonymizationSaltMinVersion = 0,
  };
 ......
 // 下面是方法声明
  virtual ~ChildProcess() = default;

  ......
  virtual void BindServiceInterface(::mojo::GenericPendingReceiver receiver) = 0;

  
  virtual void BindReceiver(::mojo::GenericPendingReceiver receiver) = 0;

  ......
};

生成的c++ 类除了方法声明之外,还包括如下信息:

  • Name_接口名称
  • MessageToMethodInfo_: 方法表,通过收到的消息找到对应的rpc方法。
  • Proxy_ 代理对象,通过代理对象调用具体方法组装消息,发送给服务端。(包括方法名称,参数等,用于调用服务端对应方法)
  • Stub_ 用于描述服务端,指向服务端具体实现。
  • RequestValidator_ 请求校验。
  • ResponseValidator_ 响应校验。

通过官方文档我们知道通过Remote 调用服务端方法。 来看一下Remote 是如何实现的。

mojo/public/cpp/bindings/remote.h

cpp 复制代码
 887 void ChildProcessProxy::BindServiceInterface(
 888     ::mojo::GenericPendingReceiver in_receiver) {
 889 #if BUILDFLAG(MOJO_TRACE_ENABLED)
 890   TRACE_EVENT1(
 891     "mojom", "Send content::mojom::ChildProcess::BindServiceInterface", "input_parameters",
 892     [&](perfetto::TracedValue context){
 893       auto dict = std::move(context).WriteDictionary();
 894       perfetto::WriteIntoTracedValueWithFallback(
 895            dict.AddItem("receiver"), in_receiver,
 896                         "<value of type ::mojo::GenericPendingReceiver>");
 897    });
 898 #endif
 899   const bool kExpectsResponse = false;
 900   const bool kIsSync = false;
 901   const bool kAllowInterrupt = true;
 902 
 903   const uint32_t kFlags =
 904       ((kExpectsResponse) ? mojo::Message::kFlagExpectsResponse : 0) |
 905       ((kIsSync) ? mojo::Message::kFlagIsSync : 0) |
 906       ((kAllowInterrupt) ? 0 : mojo::Message::kFlagNoInterrupt);
 907 
 908   mojo::Message message(
 909       internal::kChildProcess_BindServiceInterface_Name, kFlags, 0, 0, nullptr);
 910   mojo::internal::MessageFragment<
 911       ::content::mojom::internal::ChildProcess_BindServiceInterface_Params_Data> params(
 912           message);
 913   params.Allocate();
 914   mojo::internal::MessageFragment<
 915       typename decltype(params->receiver)::BaseType> receiver_fragment(
 916           params.message());
 917   mojo::internal::Serialize<::mojo_base::mojom::GenericPendingReceiverDataView>(
 918       in_receiver, receiver_fragment);
 919   params->receiver.Set(
 920       receiver_fragment.is_null() ? nullptr : receiver_fragment.data());
 921   MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING(
 922       params->receiver.is_null(),
 923       mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
 924       "null receiver in ChildProcess.BindServiceInterface request");
 925 
 926 #if defined(ENABLE_IPC_FUZZER)
 927   message.set_interface_name(ChildProcess::Name_);
 928   message.set_method_name("BindServiceInterface");
 929 #endif
 930   // This return value may be ignored as false implies the Connector has
 931   // encountered an error, which will be visible through other means.
 932   ::mojo::internal::SendMojoMessage(*receiver_, message);
 933 }

BindServiceInterface 的主要作用就是组装消息,然后发送。 这里908-909行创建Message的时候指定的消息名称为internal::kChildProcess_BindServiceInterface_Name, 用于指示服务端调用哪个方法。 这里932行receiver_ 持有一个portal,可以将数据写出去。

我们再看服务端怎么处理消息:

cpp 复制代码
259 // Implements the mojom ChildProcess interface and lives on the IO thread.
260 class ChildThreadImpl::IOThreadState
261     : public base::RefCountedThreadSafe<IOThreadState>,
262       public mojom::ChildProcess {
     ......
358 
359   void BindServiceInterface(mojo::GenericPendingReceiver receiver) override {
360     if (service_binder_)
361       service_binder_.Run(&receiver);
362 
363     if (receiver) {
364       main_thread_task_runner_->PostTask(
365           FROM_HERE, base::BindOnce(&ChildThreadImpl::BindServiceInterface,
366                                     weak_main_thread_, std::move(receiver)));
367     }
368   }
369 
370   void BindReceiver(mojo::GenericPendingReceiver receiver) override {
371     if (wait_for_interface_binders_) {
372       pending_binding_requests_.push_back(std::move(receiver));
373       return;
374     }
375 
376     if (interface_binders_.TryBind(&receiver))
377       return;
378 
379     main_thread_task_runner_->PostTask(
380         FROM_HERE, base::BindOnce(&ChildThreadImpl::OnBindReceiver,
381                                   weak_main_thread_, std::move(receiver)));
382   }
383 
      ......

457   mojo::Receiver<mojom::ChildProcess> receiver_{this};
458 
459   // Binding requests which should be handled by |interface_binders|, but which
460   // have been queued because |allow_interface_binders_| is still |false|.
461   std::vector<mojo::GenericPendingReceiver> pending_binding_requests_;
462 };

ChildThreadImpl::IOThreadState 实现了mojom::ChildProcess接口。 它持有了一个receiver_对象, 该对象持有portal一端, 用于处理对端发送的消息。

我们具体来看 mojo::Receivermojom::ChildProcess 的实现。

cpp 复制代码
template <typename Interface,
          typename ImplRefTraits = RawPtrImplRefTraits<Interface>>
class Receiver {
......
      explicit Receiver(ImplPointerType impl) : internal_state_(std::move(impl)) {}
......
 private:
  internal::BindingState<Interface, ImplRefTraits> internal_state_;
}

Receiver 的参数为ChildThreadImpl::IOThreadState实例。 然后初始化internal_state_变量。internal_state_类型展开宏之后为nternal::BindingState<mojom::ChildProcess, RawPtrImplRefTraitsmojom::ChildProcess> internal_state_。

cpp 复制代码
template <typename Interface, typename ImplRefTraits>
class BindingState : public BindingStateBase {
 public:
  using ImplPointerType = typename ImplRefTraits::PointerType;

  explicit BindingState(ImplPointerType impl) {
    stub_.set_sink(std::move(impl));
  }

......
}

这里stub_成员变量为 typename Interface::template Stub_ stub_;展开宏为

mojom::ChildProcess::Stub_ stub_, 也就是ChildProcessStub。 我们看一下它的实现以及set_sink 方法。

out/Default/gen/content/common/child_process.mojom.h

cpp 复制代码
template <typename ImplRefTraits =
              mojo::RawPtrImplRefTraits<ChildProcess>>
class ChildProcessStub
    : public mojo::MessageReceiverWithResponderStatus {
 public:
........
  void set_sink(ImplPointerType sink) { sink_ = std::move(sink); }
  ImplPointerType& sink() { return sink_; }

  bool Accept(mojo::Message* message) override {
    if (ImplRefTraits::IsNull(sink_))
      return false;
    return ChildProcessStubDispatch::Accept(
        ImplRefTraits::GetRawPointer(&sink_), message);
  }

  bool AcceptWithResponder(
      mojo::Message* message,
      std::unique_ptr<mojo::MessageReceiverWithStatus> responder) override {
    if (ImplRefTraits::IsNull(sink_))
      return false;
    return ChildProcessStubDispatch::AcceptWithResponder(
        ImplRefTraits::GetRawPointer(&sink_), message, std::move(responder));
  }

 private:
  ImplPointerType sink_;
};

set_sink方法实际设置成员变量sink_, 实际指向ChildThreadImpl::IOThreadState实例。 当receiver收到消息后会调用Accept(异步)方法或者AcceptWithResponder(同步返回结果)方法。

Accept方法调用ChildProcessStubDispatch::Accept方法,第一个参数为sink_, 第二个参数为消息体。我们来看ChildProcessStubDispatch::Accept 方法

out/Default/gen/content/common/child_process.mojom.cc

cpp 复制代码
1021 // static
1022 bool ChildProcessStubDispatch::Accept(
1023     ChildProcess* impl,
1024     mojo::Message* message) {
1025   switch (message->header()->name) {
         .......
1182     case internal::kChildProcess_BindServiceInterface_Name: {
1183 
1184       DCHECK(message->is_serialized());
1185       internal::ChildProcess_BindServiceInterface_Params_Data* params =
1186           reinterpret_cast<internal::ChildProcess_BindServiceInterface_Params_Data*>(
1187               message->mutable_payload());
1188 
1189       bool success = true;
1190       ::mojo::GenericPendingReceiver p_receiver{};
1191       ChildProcess_BindServiceInterface_ParamsDataView input_data_view(params, message);
1192 
1193       if (success && !input_data_view.ReadReceiver(&p_receiver))
1194         success = false;
1195       if (!success) {
1196         ReportValidationErrorForMessage(
1197             message,
1198             mojo::internal::VALIDATION_ERROR_DESERIALIZATION_FAILED,
1199             ChildProcess::Name_, 6, false);
1200         return false;
1201       }
1202       // A null |impl| means no implementation was bound.
1203       DCHECK(impl);
1204       impl->BindServiceInterface(
1205 std::move(p_receiver));
1206       return true;
1207     }
1208     case internal::kChildProcess_BindReceiver_Name: {
1209 
1210       DCHECK(message->is_serialized());
1211       internal::ChildProcess_BindReceiver_Params_Data* params =
1212           reinterpret_cast<internal::ChildProcess_BindReceiver_Params_Data*>(
1213               message->mutable_payload());
1214 
1215       bool success = true;
1216       ::mojo::GenericPendingReceiver p_receiver{};
1217       ChildProcess_BindReceiver_ParamsDataView input_data_view(params, message);
1218 
1219       if (success && !input_data_view.ReadReceiver(&p_receiver))
1220         success = false;
1221       if (!success) {
1222         ReportValidationErrorForMessage(
1223             message,
1224             mojo::internal::VALIDATION_ERROR_DESERIALIZATION_FAILED,
1225             ChildProcess::Name_, 7, false);
1226         return false;
1227       }
1228       // A null |impl| means no implementation was bound.
1229       DCHECK(impl);
1230       impl->BindReceiver(
1231 std::move(p_receiver));
1232       return true;
1233     }
      .......
1259     }
1260   }
1261   return false;
1262 }
                                                                                                              

函数很简单,根据message的名称反序列化参数,然后调用ChildThreadImpl::IOThreadState的对应方法。

以上就是典型的ipcz binding 使用。 整体通信借助ipcz 通道。

相关推荐
獨枭16 小时前
Linux 下安装和使用 Jupyter Notebook
linux·chrome·jupyter
日升1 天前
Chrome 134 版本开发者工具(DevTools)更新内容
前端·chrome·浏览器
我要升天!2 天前
Linux中《环境变量》详细介绍
linux·运维·chrome
muzidigbig5 天前
Chrome(Google) 浏览器安装Vue2、Vue3 Devtools插件方法
chrome·vue.js devtools·google vue插件方法
pitt19975 天前
Chrome 开发环境快速屏蔽 CORS 跨域限制!
chrome·跨域·cors·解决跨越技巧
skywalk81635 天前
自动化浏览器的测试框架playwright 支持多种浏览器Chromium、Firefox 和 WebKit
前端·chrome·自动化·测试·playwright
亿牛云爬虫专家6 天前
Headless Chrome 优化:减少内存占用与提速技巧
前端·chrome·内存·爬虫代理·代理ip·headless·大规模数据采集
小白学大数据7 天前
Python + Chrome 爬虫:如何抓取 AJAX 动态加载数据?
开发语言·chrome·爬虫·python
头发尚存的猿小二8 天前
Linux--环境变量
前端·javascript·chrome
Ustinian_3108 天前
【HTML】KaTeX 常用公式字符
前端·chrome·html