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

相关推荐
小诸葛的博客1 小时前
详解Linux中的定时任务管理工具crond
linux·运维·chrome
~heart将心比心5 小时前
chrome://inspect/#devices 调试 HTTP/1.1 404 Not Found 如何解决
前端·chrome
Bruce_Liuxiaowei5 小时前
使用Python脚本在Mac上彻底清除Chrome浏览历史:开发实战与隐私保护指南
chrome·python·macos
浪裡遊14 小时前
Linux常用指令
linux·运维·服务器·chrome·功能测试
鸿蒙布道师18 小时前
OpenAI为何觊觎Chrome?AI时代浏览器争夺战背后的深层逻辑
前端·人工智能·chrome·深度学习·opencv·自然语言处理·chatgpt
袈裟和尚18 小时前
如何在安卓平板上下载安装Google Chrome【轻松安装】
前端·chrome·电脑
HtwHUAT1 天前
五、web自动化测试01
前端·css·chrome·python·功能测试·selenium·html
浏览器爱好者1 天前
如何下载适用于语音识别功能增强的Google Chrome浏览器
人工智能·chrome·语音识别
yinzhiqing1 天前
ubuntu24设置拼音输入法,解决chrome不能输入中文
前端·数据库·chrome
葛立国2 天前
Mojo与Services入门指南
chrome