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 通道。

相关推荐
微wx笑10 小时前
chrome扩展程序如何实现国际化
前端·chrome
CUIYD_198910 小时前
Chrome 浏览器(版本号49之后)‌解决跨域问题
前端·chrome
Dontla1 天前
华为昇腾服务器(固件版本查询、驱动版本查询、CANN版本查询)
运维·服务器·chrome
JsenLong1 天前
ubuntu 守护进程
linux·chrome·ubuntu
前端大全1 天前
Chrome 推出全新的 DOM API,彻底革新 DOM 操作!
前端·chrome
林的快手1 天前
CSS文本属性
前端·javascript·css·chrome·node.js·css3·html5
码农君莫笑1 天前
Linux系统上同时打印到物理打印机并生成PDF副本方法研究
linux·前端·chrome·打印·信管通
代码轨迹2 天前
青龙面板运行selenium启动Chrome报错
chrome·python·selenium
三月七(爱看动漫的程序员)2 天前
与本地电脑PDF文档对话的PDF问答程序
前端·人工智能·chrome·gpt·搜索引擎·pdf·知识图谱
码界领航2 天前
【2025最新版】Chrome谷歌浏览器如何能恢复到之前的旧版本
前端·chrome