首页 > 编程语言 >Chromium源码阅读:深入理解Mojo框架的设计思想,并掌握其基本用法(2)

Chromium源码阅读:深入理解Mojo框架的设计思想,并掌握其基本用法(2)

时间:2024-06-12 18:28:23浏览次数:23  
标签:mojo Dispatcher virtual Mojo API 消息 Chromium 源码

我们继续分析Chromium的Mojo模块。

Dispatcher

Dispatcher 是 Mojo IPC 系统中的一个关键概念。它是一个虚基类类(或接口),用于实现与特定 MojoHandle 相关联的 Mojo 核心 API 调用。在 Mojo 系统中,应用程序通过这些 API 与各种类型的 IPC 机制进行交互,如消息管道、共享缓冲区、数据管道和事件观察器。

每个 MojoHandle 是系统中某个 Dispatcher 实现的不透明引用。这意味着,当你在应用程序中持有一个 MojoHandle,实际上你是在引用一个背后具体实现了 Dispatcher 接口的对象。这个对象负责处理与该 MojoHandle 关联的所有操作,例如发送和接收消息、管理共享内存或者控制数据流。
简而言之,Dispatcher 是 Mojo 系统中将句柄抽象化并连接到具体功能的胶合层。

Dispatcher的主要虚函数如下:

class MOJO_SYSTEM_IMPL_EXPORT Dispatcher
    : public base::RefCountedThreadSafe<Dispatcher> {
 public:
  enum class Type {
    UNKNOWN = 0,
    MESSAGE_PIPE,
    DATA_PIPE_PRODUCER,
    DATA_PIPE_CONSUMER,
    SHARED_BUFFER,
    WATCHER,
    INVITATION,
    // "Private" types (not exposed via the public interface):
    PLATFORM_HANDLE = -1,
  };

  Dispatcher(const Dispatcher&) = delete;
  Dispatcher& operator=(const Dispatcher&) = delete;

  // TODO(crbug.com/40778522): Remove these and all callers.
  //
  // The assert is invoked at various points of handle deserialization failure.
  // Such failures are expected and innocuous when destroying unread or unsent,
  // discarded messages with attachments that may no longer be valid; but they
  // are problematic when hit during normal message deserialization for messages
  // the application expects to read and dispatch. Both this setter and the
  // assertion are concerned only with their calling thread.
  static void SetExtractingHandlesFromMessage(bool extracting);
  static void AssertNotExtractingHandlesFromMessage();

  // All Dispatchers must minimally implement these methods.

  virtual Type GetType() const = 0;
  virtual MojoResult Close() = 0;

  / Watcher API 

  // Supports the |MojoAddTrigger()| API if implemented by this Dispatcher.
  // |dispatcher| is the resolved Dispatcher implementation from the given
  // MojoHandle to watch. The remaining arguments correspond directly to
  // arguments on the original |MojoAddTrigger()| API call. See
  // |MojoAddTrigger()| documentation.
  virtual MojoResult WatchDispatcher(scoped_refptr<Dispatcher> dispatcher,
                                     MojoHandleSignals signals,
                                     MojoTriggerCondition condition,
                                     uintptr_t context);

  // Supports the |MojoRemoveTrigger()| API if implemented by this Dispatcher.
  // Arguments correspond directly to arguments on the original
  // |MojoRemoveTrigger()| API call. See |MojoRemoveTrigger()| documentation.
  virtual MojoResult CancelWatch(uintptr_t context);

  // Supports the |MojoArmTrap()| API if implemented by this Dispatcher.
  // Arguments correspond directly to arguments on the original |MojoArmTrap()|
  // API call. See |MojoArmTrap()| documentation.
  virtual MojoResult Arm(uint32_t* num_blocking_events,
                         MojoTrapEvent* blocking_events);

  / Message pipe API /

  // Supports the |MojoWriteMessage()| API if implemented by this Dispatcher.
  // |message| is the message object referenced by the MojoMessageHandle passed
  // to the original API call. See |MojoWriteMessage()| documentation.
  virtual MojoResult WriteMessage(
      std::unique_ptr<ports::UserMessageEvent> message);

  // Supports the |MojoReadMessage()| API if implemented by this Dispatcher.
  // If successful, |*message| contains a newly read message object, which will
  // be yielded to the API caller as an opaque MojoMessageHandle value. See
  // |MojoReadMessage()| documentation.
  virtual MojoResult ReadMessage(
      std::unique_ptr<ports::UserMessageEvent>* message);

  / Shared buffer API /

  // Supports the |MojoDuplicateBufferHandle()| API if implemented by this
  // Dispatcher.
  //
  // |options| may be null. |new_dispatcher| must not be null, but
  // |*new_dispatcher| should be null (and will contain the dispatcher for the
  // new handle on success).
  //
  // See |MojoDuplicateBufferHandle()| documentation.
  virtual MojoResult DuplicateBufferHandle(
      const MojoDuplicateBufferHandleOptions* options,
      scoped_refptr<Dispatcher>* new_dispatcher);

  // Supports the |MojoMapBuffer()| API if implemented by this Dispatcher.
  // |offset| and |num_bytes| correspond to arguments given to the original API
  // call. On success, |*mapping| will contain a memory mapping that Mojo Core
  // will internally retain until the buffer is unmapped by |MojoUnmapBuffer()|.
  // See |MojoMapBuffer()| documentation.
  virtual MojoResult MapBuffer(
      uint64_t offset,
      uint64_t num_bytes,
      std::unique_ptr<PlatformSharedMemoryMapping>* mapping);

  // Supports the |MojoGetBufferInfo()| API if implemented by this Dispatcher.
  // Arguments correspond to the ones given to the original API call. See
  // |MojoGetBufferInfo()| documentation.
  virtual MojoResult GetBufferInfo(MojoSharedBufferInfo* info);

  / Data pipe consumer API /

  // Supports the the |MojoReadData()| API if implemented by this Dispatcher.
  // Arguments correspond to the ones given to the original API call. See
  // |MojoReadData()| documentation.
  virtual MojoResult ReadData(const MojoReadDataOptions& options,
                              void* elements,
                              uint32_t* num_bytes);

  // Supports the the |MojoBeginReadData()| API if implemented by this
  // Dispatcher. Arguments correspond to the ones given to the original API
  // call. See |MojoBeginReadData()| documentation.
  virtual MojoResult BeginReadData(const void** buffer,
                                   uint32_t* buffer_num_bytes);

  // Supports the the |MojoEndReadData()| API if implemented by this Dispatcher.
  // Arguments correspond to the ones given to the original API call. See
  // |MojoEndReadData()| documentation.
  virtual MojoResult EndReadData(uint32_t num_bytes_read);

  / Data pipe producer API /

  // Supports the the |MojoWriteData()| API if implemented by this Dispatcher.
  // Arguments correspond to the ones given to the original API call. See
  // |MojoWriteData()| documentation.
  virtual MojoResult WriteData(const void* elements,
                               uint32_t* num_bytes,
                               const MojoWriteDataOptions& options);

  // Supports the the |MojoBeginWriteData()| API if implemented by this
  // Dispatcher. Arguments correspond to the ones given to the original API
  // call. See |MojoBeginWriteData()| documentation.
  virtual MojoResult BeginWriteData(void** buffer,
                                    uint32_t* buffer_num_bytes,
                                    MojoBeginWriteDataFlags flags);

  // Supports the the |MojoEndWriteData()| API if implemented by this
  // Dispatcher. Arguments correspond to the ones given to the original API
  // call. See |MojoEndWriteData()| documentation.
  virtual MojoResult EndWriteData(uint32_t num_bytes_written);

  // Supports the |MojoAttachMessagePipeToInvitation()| API if implemented by
  // this Dispatcher. Arguments correspond to the ones given to the original API
  // call. See |MojoAttachMessagePipeToInvitation()| documentation.
  virtual MojoResult AttachMessagePipe(std::string_view name,
                                       ports::PortRef remote_peer_port);

  // Supports the |MojoExtractMessagePipeFromInvitation()| API if implemented by
  // this Dispatcher. Arguments correspond to the ones given to the original API
  // call. See |MojoExtractMessagePipeFromInvitation()| documentation.
  virtual MojoResult ExtractMessagePipe(std::string_view name,
                                        MojoHandle* message_pipe_handle);

  // Supports the |MojoSetQuota()| API if implemented by this Dispatcher.
  // Arguments correspond to the ones given to the original API call. See
  // |MojoSetQuota()| documentation.
  virtual MojoResult SetQuota(MojoQuotaType type, uint64_t limit);

  // Supports the |MojoQueryQuota()| API if implemented by this Dispatcher.
  // Arguments correspond to the ones given to the original API call. See
  // |MojoQueryQuota()| documentation.
  virtual MojoResult QueryQuota(MojoQuotaType type,
                                uint64_t* limit,
                                uint64_t* usage);

  / General-purpose API for all handle types /

  // Gets the current handle signals state. (The default implementation simply
  // returns a default-constructed |HandleSignalsState|, i.e., no signals
  // satisfied or satisfiable.) Note: The state is subject to change from other
  // threads.
  virtual HandleSignalsState GetHandleSignalsState() const;

  // Adds a WatcherDispatcher reference to this dispatcher, to be notified of
  // all subsequent changes to handle state including signal changes or closure.
  // The reference is associated with a |context| for disambiguation of
  // removals.
  virtual MojoResult AddWatcherRef(
      const scoped_refptr<WatcherDispatcher>& watcher,
      uintptr_t context);

  // Removes a WatcherDispatcher reference from this dispatcher.
  virtual MojoResult RemoveWatcherRef(WatcherDispatcher* watcher,
                                      uintptr_t context);

  // Informs the caller of the total serialized size (in bytes) and the total
  // number of platform handles and ports needed to transfer this dispatcher
  // across a message pipe.
  //
  // Must eventually be followed by a call to EndSerializeAndClose(). Note that
  // StartSerialize() and EndSerialize() are always called in sequence, and
  // only between calls to BeginTransit() and either (but not both)
  // CompleteTransitAndClose() or CancelTransit().
  //
  // For this reason it is IMPERATIVE that the implementation ensure a
  // consistent serializable state between BeginTransit() and
  // CompleteTransitAndClose()/CancelTransit().
  virtual void StartSerialize(uint32_t* num_bytes,
                              uint32_t* num_ports,
                              uint32_t* num_platform_handles);

  // Serializes this dispatcher into |destination|, |ports|, and |handles|.
  // Returns true iff successful, false otherwise. In either case the dispatcher
  // will close.
  //
  // NOTE: Transit MAY still fail after this call returns. Implementations
  // should not assume PlatformHandle ownership has transferred until
  // CompleteTransitAndClose() is called. In other words, if CancelTransit() is
  // called, the implementation should retain its PlatformHandles in working
  // condition.
  virtual bool EndSerialize(void* destination,
                            ports::PortName* ports,
                            PlatformHandle* handles);

  // Does whatever is necessary to begin transit of the dispatcher.  This
  // should return |true| if transit is OK, or false if the underlying resource
  // is deemed busy by the implementation.
  virtual bool BeginTransit();

  // Does whatever is necessary to complete transit of the dispatcher, including
  // closure. This is only called upon successfully transmitting an outgoing
  // message containing this serialized dispatcher.
  virtual void CompleteTransitAndClose();

  // Does whatever is necessary to cancel transit of the dispatcher. The
  // dispatcher should remain in a working state and resume normal operation.
  virtual void CancelTransit();

  // Deserializes a specific dispatcher type from an incoming message.
  static scoped_refptr<Dispatcher> Deserialize(Type type,
                                               const void* bytes,
                                               size_t num_bytes,
                                               const ports::PortName* ports,
                                               size_t num_ports,
                                               PlatformHandle* platform_handles,
                                               size_t platform_handle_count);

 protected:
  friend class base::RefCountedThreadSafe<Dispatcher>;

  Dispatcher();
  virtual ~Dispatcher();

看到了ReadMessage、WriteMessage、MapBuffer等高级Mojo的原语,另外看到了一组消息类型:

enum class Type {
  UNKNOWN = 0,
  MESSAGE_PIPE,
  DATA_PIPE_PRODUCER,
  DATA_PIPE_CONSUMER,
  SHARED_BUFFER,
  WATCHER,
  INVITATION,

  // "Private" types (not exposed via the public interface):
  PLATFORM_HANDLE = -1,
};

跟随线索可以发现这些消息的Dispatcher:

  • MessagePipeDispatcher: 管理消息管道的 Dispatcher,它允许两个 Mojo 句柄之间传递消息。
  • SharedBufferDispatcher: 管理共享内存缓冲区的 Dispatcher,允许跨 Mojo 句柄共享内存。
  • DataPipeConsumerDispatcher: 管理数据管道的消费端的 Dispatcher,它允许从数据管道读取数据。
  • DataPipeProducerDispatcher: 管理数据管道的生产端的 Dispatcher,它允许向数据管道写入数据。
  • WatcherDispatcher: 管理事件观察的 Dispatcher,通常用于异步通知某些事件发生。
  • InvitationDispatcher: 管理进程间邀请的 Dispatcher,用于建立进程间的连接和通信。

通过这些不同的 Dispatcher 实现,Mojo IPC 提供了一个多样化和灵活的方式来处理跨进程通信的各种需求。

Mojo的Message

和Message相关的源文件(主线索):
在这里插入图片描述
先看Message.h的Message类:

这个 Message 类在 Mojo IPC 系统中的作用是封装要通过消息管道 (MessagePipe)
发送的数据和句柄。Message
对象拥有自己的数据和句柄,并且允许消费者(即消息的接收者)更改这些数据和句柄。消息的数据由一个头部和随后的有效载荷组成。

下面是 Message 类的主要特点和功能:

  • 标志位 (kFlagExpectsResponse, kFlagIsResponse, 等): 这些常量定义了消息的不同行为,如是否期望响应、是否是响应消息、是否是同步消息等。

  • 构造函数: Message 类提供了多个构造函数,用于创建不同类型的消息。有的构造函数用于创建未初始化的消息,有的用于创建已序列化的消息对象,还有的用于从现有的消息句柄创建消息。

  • 移动构造函数和移动赋值运算符 (Message(Message&& other)operator=(Message&& other)): 允许 Message
    对象之间的移动语义,这样可以有效地在不同的上下文中传递消息,而不需要复制整个消息内容。

  • Reset 方法: 将 Message 对象重置为未初始化状态,这样它就不再包含任何数据或句柄。

  • IsNull 方法: 检查消息是否未初始化。

  • IsValid 方法: 检查消息是否处于有效状态。一条消息如果在构建过程中遇到部分反序列化失败,则可能处于无效状态。

  • is_serialized 方法: 检查消息是否已序列化。

  • 数据访问方法 (data, mutable_data, data_num_bytes): 提供对消息数据的只读和可写访问,以及查询消息数据的字节大小。

Message 类是 Mojo IPC
的核心组件之一,它允许以一种结构化和类型安全的方式来封装和传输数据。通过序列化和反序列化机制,Message
在进程间的通信中起着桥梁的作用,确保数据和句柄的正确传递和解析。

Message这个类在BindingBase工程中,是为Binding服务的。接下来我们看看Binding原理。

Mojo的Binding

之前分析鼠标消息的时候,已经初步接触到了Binding的一些细节。
为了实现高级跨进程通信抽象,Mojom会通过编译一个.mojom的源文件,生成对应的客户端和服务端代码,使其可以像进程内普通对象一样调用。
我们以compositor_frame_sink.mojom为例。
compositor_frame_sink.mojom的定义如下:

// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

module viz.mojom;

import "mojo/public/mojom/base/time.mojom";
import "mojo/public/mojom/base/shared_memory.mojom";
import ... 略

// Tags the frame sink with the type of source producing its content.
enum CompositorFrameSinkType {
  kUnspecified,
  kVideo,
  kMediaStream,
  kLayerTree,
};

// A CompositorFrameSink is an interface for receiving CompositorFrame
// structs. A CompositorFrame contains the complete output meant for display.
// Each time a client has a graphical update, and receives an OnBeginFrame, it
// is responsible for creating a CompositorFrame to update its portion of the
// screen.
interface CompositorFrameSink {
  // Lets the display compositor know that the client wishes to receive the next
  // BeginFrame event.
  SetNeedsBeginFrame(bool needs_begin_frame);

//...略
}
//...略

mojom 的语法和C++26非常相似,这也是让Chromium开发者几乎不用任何学习就可以编写mojom的代码。

通过 mojom_bindings_generator.py,mojom 会生成2个源文件,4个关键类:

  • CompositorFrameSink
  • CompositorFrameSinkStub
  • CompositorFrameSinkProxy
  • CompositorFrameSinkStubDispatch

这四个类展开来说:

  1. CompositorFrameSink: 这是由 .mojom 文件定义的接口转换而成的一个抽象基类。它定义了接口的方法,但不提供具体的实现。服务端(Impl 端)需要提供一个派生自
    CompositorFrameSink 的具体类,实现所有的虚拟方法。客户端(Proxy 端)会使用这个接口与服务端通信。

  2. CompositorFrameSinkStub: 这是一个存根类,其作用是接收传入的消息并将它们转换为对应的 CompositorFrameSink 方法调用。在服务端,每当一个消息到达时,CompositorFrameSinkStub
    会将消息反序列化,然后调用在 CompositorFrameSink 实现类中对应的方法。Stub 类通常由 Mojo
    工具链自动生成,并与一个 Mojo 句柄关联,以便监听进入的消息。

  3. CompositorFrameSinkProxy: 这是客户端的代理类,它实现了 CompositorFrameSink 接口。客户端通过调用 CompositorFrameSinkProxy
    的方法,将方法调用转换为消息,并将这些消息发送到服务端。Proxy 类负责序列化方法调用的参数,创建消息,并通过 Mojo
    消息管道将其发送出去。Proxy 类也是由 Mojo 工具链自动生成的。

  4. CompositorFrameSinkStubDispatch: 这是一个辅助类,其作用是根据接收到的消息确定应该调用 CompositorFrameSink 的哪个具体方法。它通常包含一个静态方法,比如 Accept 或者
    AcceptWithResponder,这些方法通过检查消息中的方法 ID 和解析参数来分发调用。StubDispatch 常常在
    CompositorFrameSinkStub 内部使用,作为消息分发机制的一部分。

总的来说,这些类共同协作,为 Mojo 接口的调用提供了一个完整的生命周期管理:

  • 客户端: 通过 CompositorFrameSinkProxy 发送消息。
  • 服务端: 通过 CompositorFrameSinkStub 接收消息,并通过 CompositorFrameSinkStubDispatch 分发到具体的 CompositorFrameSink 实现。
  • 服务端实现: 实现 CompositorFrameSink 接口的具体业务逻辑。

这样的设计模式使得开发者可以专注于实现业务逻辑,而无需关心底层的消息传输和序列化细节。

为了验证这个过程,我们接下来可以分别打两个断点。

首先看客户端这边的,在CompositorFrameSinkProxy下断点,堆栈如下在这里插入图片描述
序列化主要涉及参数的序列化,关键代码(生成的代码)如下:
在这里插入图片描述
截图中看到了MessageFragment类,也简单翻译一下这个类的说明:

MessageFragment 类在 Mojo IPC (Inter-Process Communication)
系统中的作用是为消息对象 (Message)中的序列化代码提供一个通用接口,用于分配、初始化,并方便地访问对齐的数据块。MessageFragment
对应于消息中的一个逻辑数据元素,例如结构体(struct)、字段(field)、数组、数组元素等。

MessageFragment 在构造时配置为具有部分序列化的 Message。最初,MessageFragment
是空的,不引用任何有效的内存区域。

要使用 data()operator-> 访问数据,必须首先在消息中分配一块内存。这可以通过调用 Allocate()
方法来完成,该方法在消息有效载荷的末尾追加 sizeof(T) 字节,并控制这些字节;或者通过调用 Claim()
方法来完成,它接受消息有效载荷中的现有指针,并控制该消息偏移处的前 sizeof(T)
字节。无论使用哪种方式,都会在声明的字节上构造一个新的 T 对象,之后可以使用这个 MessageFragment 读取或修改它。

对于数组类型,使用这个类的特化版本(在下面定义),并且必须调用 AllocateArrayData() 方法来分配和声明消息中的空间。

总结一下,MessageFragment 的主要用途和功能包括:

  • 为消息中的逻辑数据元素提供内存分配和初始化。
  • 通过 Allocate() 方法在消息末尾追加数据并控制该数据。
  • 通过 Claim() 方法来接管消息中已存在的内存区域。
  • 提供对分配内存的直接访问,允许序列化代码读取和修改数据。
  • 对于数组类型数据,提供特化的分配和访问机制。

到了服务端,在Impl对应的函数上断点:
在这里插入图片描述

CompositorFrameSinkImpl 是 CompositorFrameSink 接口的服务端具体实现类。在 Mojo IPC 系统中,当服务端需要提供 CompositorFrameSink 接口的具体实现时,它会实现一个类似于 CompositorFrameSinkImpl 的类。这个类的职责包括:

  • 实现接口:实现 CompositorFrameSink 定义的所有方法,例如处理客户端的 SubmitCompositorFrame调用。
  • 处理逻辑:包含处理提交的合成帧的逻辑,这可能涉及到合成操作、资源管理和与硬件加速图形系统的交互。
  • Mojo 绑定:通过 Mojo绑定与客户端通信,接收请求和发送响应。

参数的解包比较简单,通过CompositorFrameSink_SetNeedsBeginFrame_Params_Data和CompositorFrameSink_SetNeedsBeginFrame_ParamsDataView即可从payload中获取参数:
在这里插入图片描述

可以印证前面的结论。

实例的创建

那么,在Prox的调用,通过指定方法ID,并将参数序列化,通过Message,将调用信息发送到Impl端,Impl端收到Message后经过Dispatch等一系列的路由,到了xxxStub,最终到了Impl,再到CompositorFrameSinkSupport。

那么,客户端(Prox)是如何创建相应的实例呢?服务端(Impl)又是如何跟xxxStub
绑定的呢?带着疑问,我们在构造函数打断点,并得出以下结论:

在 Mojo IPC
系统中,客户端(Proxy)和服务端(Impl)通过一系列的步骤来创建实例和建立绑定。这些步骤涉及到接口的定义、代理和存根的生成、实例的创建、消息的发送与接收、以及最终的方法调用。下面是如何在客户端创建代理实例和服务端绑定存根的详细步骤:

客户端 (Proxy) 创建实例的过程:

  1. 定义接口:首先,需要在 .mojom 文件中定义一个 Mojo 接口,这个接口包含了可供调用的方法。

  2. 生成代理和存根:使用 mojom_bindings_generator.py 脚本根据 .mojom 文件生成代理(Proxy)和存根(Stub)的源代码。

  3. 创建代理实例:在客户端代码中,使用生成的 CompositorFrameSinkProxy 类创建一个代理实例。这通常涉及到创建一个 InterfacePtr<CompositorFrameSink>,它是一个智能指针,管理对
    CompositorFrameSinkProxy 实例的引用。

  4. 建立连接:客户端通过 Mojo 的绑定机制建立与服务端的连接。这通常涉及到调用一些形式的 Bind 方法,它接收一个 InterfaceRequest,这是一个未完成的连接请求。以CompositorFrameSink为例,客户端持有一个 mojo::AssociatedRemote<viz::mojom::CompositorFrameSink> compositor_frame_sink_associated_; 指针,这个指针通过绑定 mojo::PendingAssociatedRemote<viz::mojom::CompositorFrameSink> compositor_frame_sink_associated_remote; 之后,即可开始调用。代码如下: 在这里插入图片描述

  5. 发送消息:客户端通过代理实例调用 CompositorFrameSink 接口的方法。代理会序列化这些方法调用和参数,并通过消息管道发送到服务端。

服务端 (Impl) 绑定存根的过程:

  1. 实现接口:服务端实现了在 .mojom 文件中定义的 CompositorFrameSink 接口。这个实现通常是 CompositorFrameSinkImpl 类。

  2. 创建实现实例并绑定Impl:服务端创建 CompositorFrameSinkImpl 的实例,这个实例包含了接口方法的具体逻辑。其构造函数完成了于CompositorFrameSinkImpl与CompositorFrameSink的绑定在这里插入图片描述
    绑定的本质是响应来自客户端的请求,请求里带了两个关键参数:
    mojo::PendingReceiver<mojom::CompositorFrameSink> receiver, mojo::PendingRemote<mojom::CompositorFrameSinkClient> client

  3. 创建并绑定存根:服务端使用生成的 CompositorFrameSinkStub 类,并将其与 InterfaceRequest 绑定。这个 Stub 对象负责监听进入的消息,并将它们分派到
    CompositorFrameSinkImpl 实例的方法中。在这里插入图片描述

  4. 处理和分派消息:每当服务端的 Stub 收到消息时,它将消息反序列化并使用 CompositorFrameSinkStubDispatch 将调用分派到 CompositorFrameSinkImpl
    的适当方法上。

通过上述步骤,客户端和服务端可以建立起一套 IPC 机制,并通过 Mojo
接口进行清晰和高效的跨进程通信。客户端的代理实例可以将方法调用转换为消息并发送,而服务端的存根实例则负责接收消息、分派调用和执行具体的业务逻辑。

对了,Chromium多进程模型对打断点很不友好,只需要增加命令行参数–single-process即可以单进程模式运行,这样就不会错过断点了。
另外,直接跑Chromium.exe会有很多Chromium应用层的逻辑,很重,可以改为直接调试content_shell.exe。

为什么Mojo不用ProtoBuffer,反而要自己设计一套序列化和消息定义?

Mojo 是一个为 Chromium 项目量身定制的 IPC(Inter-Process Communication,跨进程通信)系统。它不仅包含了序列化和反序列化的能力,还提供了一整套用于高效跨进程消息传递和接口定义的机制。Mojo 的设计在满足 Chromium 特定需求的同时,还解决了一些 Protobuf 在这种用例中可能面临的局限性:

  1. 零拷贝传输:
    Mojo 专注于支持大数据量的高效传输,包括对共享内存和跨进程直接内存访问的支持。这种零拷贝传输方式对于浏览器中图形和媒体相关的数据非常重要,可以最小化延迟和CPU开销。

  2. 句柄和资源的传递:
    Mojo 允许在进程间传递操作系统句柄(如文件、共享内存段和同步原语)。Protobuf 不支持这种复杂的句柄传递,而这对于浏览器中的很多操作是必需的。

  3. 同步和异步消息的支持:
    Mojo 支持同步和异步消息模式。在某些情况下,同步调用对于保持状态的一致性和避免竞态条件是必要的,而 Protobuf 本身并不处理消息的传递机制。

  4. 接口定义和版本控制:
    Mojo 允许通过 .mojom 文件定义清晰的接口和它们的方法,这些定义非常适合用于生成各种语言的绑定和接口实现。Mojo 还支持接口的版本控制,允许向后兼容的接口演进。

  5. 性能和资源利用:
    Mojo 为高性能 IPC 而设计,特别考虑了低延迟和高吞吐量的需求。在浏览器环境中,性能和资源利用是关键考量因素。

  6. 定制化需求:
    Chromium 项目有特定的需求,无法通过使用通用的序列化库(如 Protobuf)来满足。Mojo 设计为可以与 Chromium 的其他部分紧密集成,提供了更多的灵活性和控制。

  7. 安全性:
    Mojo 在设计时就考虑了沙盒和安全性,这对 Chromium 这样处理大量不可信输入的项目至关重要。

总结来说,虽然 Protobuf 是一个强大的序列化工具,用于多种用途和多种编程语言,但是 Mojo 是专门为 Chromium 这样的大型、性能敏感的项目设计的。Mojo 通过提供一套更为细致的、针对性的 IPC 机制来满足 Chromium 的特定需求,并解决 Protobuf 在某些场景下的局限性。

关于Mojo的更多资料。

建议读者直接阅读mojo的readme文档,了解更多设计细节。
在这里插入图片描述

最后,摘录一段ReadMe文档说明建立连接的应用层调用方法:

邀请

邀请是两个进程之间引导 Mojo IPC 的手段。邀请必须通过某些特定于平台的 IPC
原语(例如Windows 命名管道或 UNIX 域套接字)进行传输,公共平台支持库为这些原语提供了一些轻量级、跨平台的抽象。

对于任何两个希望建立连接的进程,一个进程必须发送 ,OutgoingInvitation而另一个进程必须接受IncomingInvitation。发送方可以将命名消息管道句柄附加到OutgoingInvitation,而
接收方可以从其 中提取它们IncomingInvitation。

当一个进程负责启动另一个进程时,基本用法可能看起来像这样。


```cpp
#include "base/command_line.h"
#include "base/process/launch.h"
#include "mojo/public/cpp/platform/platform_channel.h"
#include "mojo/public/cpp/system/invitation.h"
#include "mojo/public/cpp/system/message_pipe.h"

mojo::ScopedMessagePipeHandle LaunchAndConnectSomething() {
  // Under the hood, this is essentially always an OS pipe (domain socket pair,
  // Windows named pipe, Fuchsia channel, etc).
  mojo::PlatformChannel channel;

  mojo::OutgoingInvitation invitation;

  // Attach a message pipe to be extracted by the receiver. The other end of the
  // pipe is returned for us to use locally.
  mojo::ScopedMessagePipeHandle pipe =
      invitation->AttachMessagePipe("arbitrary pipe name");

  base::LaunchOptions options;
  base::CommandLine command_line("some_executable")
  channel.PrepareToPassRemoteEndpoint(&options, &command_line);
  base::Process child_process = base::LaunchProcess(command_line, options);
  channel.RemoteProcessLaunchAttempted();

  OutgoingInvitation::Send(std::move(invitation), child_process.Handle(),
                           channel.TakeLocalEndpoint());
  return pipe;
}


启动的进程可以依次接受IncomingInvitation:



```cpp
#include "base/command_line.h"
#include "base/threading/thread.h"
#include "mojo/core/embedder/embedder.h"
#include "mojo/core/embedder/scoped_ipc_support.h"
#include "mojo/public/cpp/platform/platform_channel.h"
#include "mojo/public/cpp/system/invitation.h"
#include "mojo/public/cpp/system/message_pipe.h"

int main(int argc, char** argv) {
  // Basic Mojo initialization for a new process.
  mojo::core::Init();
  base::Thread ipc_thread("ipc!");
  ipc_thread.StartWithOptions(
      base::Thread::Options(base::MessagePumpType::IO, 0));
  mojo::core::ScopedIPCSupport ipc_support(
      ipc_thread.task_runner(),
      mojo::core::ScopedIPCSupport::ShutdownPolicy::CLEAN);

  // Accept an invitation.
  mojo::IncomingInvitation invitation = mojo::IncomingInvitation::Accept(
      mojo::PlatformChannel::RecoverPassedEndpointFromCommandLine(
          *base::CommandLine::ForCurrentProcess()));
  mojo::ScopedMessagePipeHandle pipe =
      invitation->ExtractMessagePipe("arbitrary pipe name");

  // etc...
  return GoListenForMessagesAndRunForever(std::move(pipe));
}

现在我们已经在两个进程之间初始化了IPC。

还请记住,绑定接口只是带有一些语义和语法糖的消息管道,因此您可以将这些原始
消息管道句柄用作 mojom 接口。例如:

// Process A
mojo::OutgoingInvitation invitation;
auto pipe = invitation->AttachMessagePipe("x");
mojo::Receiver<foo::mojom::Bar> receiver(
    &bar_impl,
    mojo::PendingReceiver<foo::mojom::Bar>(std::move(pipe)));

// Process B
auto invitation = mojo::IncomingInvitation::Accept(...);
auto pipe = invitation->ExtractMessagePipe("x");
mojo::Remote<foo::mojom::Bar> bar(
    mojo::PendingRemote<foo::mojom::Bar>(std::move(pipe), 0));

// Will asynchronously invoke bar_impl.DoSomething() in process A.
bar->DoSomething();

并且为了确保万无一失,这里的用法可以反过来:邀请发送者可以将其管道端点视为,而Remote接收者将其管道端点视为PendingReceiver要绑定的。

标签:mojo,Dispatcher,virtual,Mojo,API,消息,Chromium,源码
From: https://blog.csdn.net/hebhljdx/article/details/139626823

相关文章

  • 青否数字人直播源码代理端后台操作步骤!
    青否数字人直播源码代理端后台,我们将详细介绍一下数字人的代理端后台的详细操作步骤!1.代理端入口2.代理后台预览基本设置,账号管理,资金管理,克隆端。2.1基本设置设置一些账号的基本信息包括名称,logo,二维码等等,点击“提交”即可2.2账号管理打开账号管理,可搜索已创建的......
  • C#(asp.net) 非物质文化遗产建档管理系统设-计算机毕业设计源码91695
    摘 要信息化社会内需要与之针对性的信息获取途径,但是途径的扩展基本上为人们所努力的方向,由于站在的角度存在偏差,人们经常能够获得不同类型信息,这也是技术最为难以攻克的课题。针对非物质文化遗产建档管理系统等问题,对非物质文化遗产建档管理系统进行研究分析,然后开发设计出......
  • 最新AI系统+ChatGPT网站H5源码+AI绘画系统,DALL-E3文生图,详细图文搭建教程/文档分析/识
    目录一、文章前言系统文档 二、系统演示三、系统功能模块3.1AI全模型支持/插件系统AI模型提问文档分析​识图理解能力3.2GPts应用3.2.1GPTs应用3.2.2GPTs工作台3.2.3自定义创建预设应用3.3AI专业绘画3.3.1文生图/图生图(垫图)3.3.2 局部编辑重绘3.3.3 ......
  • 源码编译构建LAMP
    Apache起源源于APatchyServer,著名的开源Web服务软件1995年时,发布Apache服务程序的1.0版本由Apache软件基金会(ASF)负责维护最新的名称为“ApacheHTTPServer”官方站点:http://httpd.apache.org/主要特点开发源代码/跨平台应用支持多种网页编程语言模块化涉及、运行稳定、......
  • 短剧对接广告联盟app平台开发源码
    短剧对接广告联盟APP平台开发的源码涉及多方面的技术整合与策略设计。以下是一些关键步骤和考虑因素:需求分析:深入理解短剧APP的市场定位、目标用户群体以及广告联盟的具体需求。这有助于确定系统的基本功能和特色服务,确保开发的APP能够满足用户和广告主的实际需求1。系统......
  • SSM-小区物业管理系统-48954(免费领源码+开发文档)可做计算机毕业设计JAVA、PHP、爬虫、
    基于SSM小区物业管理系统摘要随着计算机科学技术日渐成熟,人们已经深刻认识到了计算机功能的强大,计算机已经进入到了人类社会发展的各个领域,并且发挥着十分重要的作用。每个社区的物业管理是一项系统而复杂的工作,它需要一个团队互相配合、分工协作。在该领域,传统的手工存取......
  • 微信小游戏开发流程及上架步骤:微信小游戏定制开发源码搭建
    微信小游戏开发方案主要包括以下几个方面:    1.游戏设计:根据需求和目标用户群体,设计游戏玩法、关卡、角色、场景等元素,确保游戏具有吸引力和可玩性。    2.技术实现:根据游戏设计,选择合适的技术栈和开发工具,进行游戏代码编写、测试、优化等工作。    ......
  • 【S087】Springboot+Thymleaf在线答疑系统项目源码 java源代码
    运行截图:登录学生注册教师注册学生发起问题联系我们后台首页常见问题管理添加常见问题人工答疑学生管理个人信息修改密码项目组成:项目源码:源码获取⬇⬇⬇......
  • 【S086】基于Springboot图书馆管理系统项目源码 java图书借阅管理 含文档
    运行截图:登录后台主页图书列表图书上架借阅图书归还图书用户列表添加用户公告列表发布公告个人信息详情个人信息编辑项目组成:项目源码:项目文档:源码获取⬇⬇⬇......
  • 龙哥量化:通达信倚天出鞘主图指标公式源码
    如果您需要代写公式,请联系我。龙哥QQ:591438821龙哥微信:Long622889 涨停:=C/REF(C,1)>1.0970ANDC=HANDC/REF(C,1)<1.11;STICKLINE(涨停,O,C,2,0),COLORYELLOW;跌停:=C/REF(C,1)<0.913ANDC=L;STICKLINE(跌停,O,C,2,0),COLORGREEN;周期:=BArslAST(REF(涨停,1)ANDNOT......