关于mojo binding的官方文档为mojo docs。 由于比较复杂,这里只做简单源码分析。
我们知道要实现rpc,必须实现客户端和服务端。 mojo 实现了一套领域语言,通过领域语言描述接口和数据, 再通过特有编译器编译成c++代码。 这个过程会生成Mojo对象, 我们以content/common/child_process.mojom 为例子来分析。
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
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
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,可以将数据写出去。
我们再看服务端怎么处理消息:
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 的实现。
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_。
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
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
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 通道。