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

我们继续分析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要绑定的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/26504.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LabVIEW 32位与64位版本比较分析:性能与兼容性详解

LabVIEW的32位和64位版本在功能、性能、兼容性和应用场景等方面存在差异。本文从系统要求、内存管理、性能、兼容性、驱动支持和开发维护等多个角度进行详细分析&#xff0c;帮助用户选择合适的版本。 一、系统要求 操作系统支持&#xff1a; 32位LabVIEW&#xff1a;可以在32位…

XL3001E1 SOP-8 3A 40V 220KHz 降压LED恒流驱动器芯片

XL3001E1是一款LED驱动芯片&#xff0c;主要用于需要稳定电流驱动的LED照明产品中。其应用领域广泛&#xff0c;包括但不限于以下几个方面&#xff1a; 1. 室内照明&#xff1a;XL3001E1可用于各种室内LED灯具&#xff0c;如球泡灯、筒灯、射灯和平板灯&#xff0c;提供恒定的电…

【C++进阶】RBTree封装map与set

1.红黑树的迭代器 1.1 begin() begin()就是红黑树的开头&#xff0c;那么对于红黑树来说按照中序序列是该树的最左节点。 Iterator Begin(){Node* leftMin _root;while (leftMin->_left){leftMin leftMin->_left;}return Iterator(leftMin);} 1.2 end() begin()就是…

好书推荐:生成式AI入门与AWS实战

这本书给LLM的爱好者者提供了完整的学习路线&#xff0c;让读者从使用大语言模型开始到剖析常用的技术概念&#xff0c;能够填补了机器学习爱好者从传统的文字处理到大语言模型的空白知识&#xff0c;包括显存计算优化&#xff0c;微调&#xff0c;RAG&#xff0c; 多模态&…

springboot vue 的在线考试系统

springboot & vue 的在线考试系统 在线考试系统&#xff0c;功能如下&#xff1a; 管理员&#xff1a;题库管理&#xff0c;支持选择题和判断题&#xff0c;考试管理&#xff0c;成绩查询&#xff0c;学生管理&#xff0c;教师管理. 教师&#xff1a;题库管理&#xff0c;…

深入解析TF-IDF算法:文本分析的基石与力量

在信息爆炸的时代文本数据无处不在&#xff0c;从新闻报道到社交媒体帖子&#xff0c;从学术论文到产品评论&#xff0c;大量的文本信息需要被有效地分析和利用。在这样的背景下TF-IDF&#xff08;Term Frequency-Inverse Document Frequency&#xff09;算法作为一种简单而有效…

抖店被扣保证金,做起来太难导致心态崩了,怎么办?

我是王路飞。 技术、黑科技这些东西&#xff0c;决定不了你做店的结果。 能够决定最终结果的&#xff0c;一定是心态&#xff0c;是乐观还是悲观&#xff1f;是自负还是自卑&#xff1f;是焦躁还是踏实&#xff1f;这很关键。 店铺被扣保证金了&#xff0c;感觉没希望了&…

DIYGW可视化开发工具:微信小程序与多端应用开发的利器

一、引言 随着移动互联网的飞速发展&#xff0c;微信小程序以其轻便、易用和跨平台的特点受到了广泛关注。然而&#xff0c;微信小程序的开发相较于传统的H5网页开发&#xff0c;在UI搭建和交互设计上存在一定的挑战。为了应对这些挑战&#xff0c;开发者们一直在寻找更加高效…

私域引流宝PHP源码 以及搭建教程

私域引流宝PHP源码 以及搭建教程

直播录制怎么录?(3个方法)

在数字化快速发展的今天&#xff0c;直播已经成为了一种重要的传播方式&#xff0c;无论是商业活动、教育培训&#xff0c;还是娱乐休闲&#xff0c;直播都展现出了其独特的价值。然而&#xff0c;直播的即时性也意味着一旦错过&#xff0c;就很难再次体验。这时&#xff0c;直…

第20篇 Intel FPGA Monitor Program的使用<三>

Q&#xff1a;如何用Intel FPGA Monitor Program创建汇编语言工程呢&#xff1f; A&#xff1a;我们用一个Nios II汇编语言简易应用程序来发掘Intel Monitor FPGA Program软件的一些功能特性&#xff0c;并介绍创建工程的基本步骤。该程序可以实现找到存储在存储器中的32位整…

怎么改图片尺寸更方便?在线图片改大小的使用方法

图片怎么快速改尺寸呢&#xff1f;在网上传图或者做其他用途时&#xff0c;经常会对图片的尺寸有要求&#xff0c;当拍摄或者制作的图片太大或者太小时&#xff0c;都会导致图片的无法正常使用&#xff0c;那么就需要按照规定将图片改大小之后才能正常使用。 在遇到图片修改大…

Epicor BAQ - BAQ设计与调用

目录 一、BAQ设计常用功能1.跨公司查询2.修改作者3.添加筛选条件4.使用BAQ参数5.子查询 二、在客制化中调用BAQ取数三、在BPM中调用BAQ取数四、结束 一、BAQ设计常用功能 1.跨公司查询 在BAQ的General页面勾选Cross-company后&#xff0c;BAQ可以跨公司查询数据。 2.修改作…

Cloudflare 错误 1006、1007、1008 解决方案 | 如何修复

根据不完全统计&#xff0c;使用 Cloudflare 的网站比例已经接近 20%。因此&#xff0c;在日常工作中&#xff0c;比如进行网页抓取时&#xff0c;您可能经常会遇到一些因 Cloudflare 而产生的困难。例如&#xff0c;遇到 Cloudflare 错误 1006、1007 和 1008&#xff0c;这些错…

水电表抄表解决方案

1.简述&#xff1a;水电表抄表方案的必要性 水电表抄表是物业管理服务中不可或缺的一环&#xff0c;它涉及到费用计算、资源优化配置及其环保节能监管等各个方面。传统的手工抄表方法不但耗时费力&#xff0c;且容易出差错&#xff0c;因而&#xff0c;现代化抄表方案是十分重…

Java——重载

一、重载&#xff08;Overload&#xff09; 1、重载是什么 方法重载&#xff08;Method Overloading&#xff09;是Java中实现多态的一种方式。它允许在同一个类中定义多个同名的方法&#xff0c;只要这些方法的参数列表不同。这些不同的参数列表可以通过不同的参数类型、参数…

构建全面框架 | 简化基因组+线粒体遗传进化联合分析

近日&#xff0c;凌恩生物客户河北农业大学、浙江大学及英国格林威治大学的研究团队合作&#xff0c;在《Insect Science》杂志上发表了题为“A comprehensive framework for the delimitation of species within the Bemisia tabaci cryptic complex, a global pest-species g…

GStreamer安装——iOS

安装iOS开发 支持从iOS6开始的所有版本 先决条件 iOS开发需要下载Xcode和iOSSDK。Xcode 可以在App Store或 这里 iOSSDK&#xff0c;如果它还没有包含在您的Xcode版本中&#xff0c; 可以从下载选项卡下的Xcode首选项菜单下载。 最低要求iOS版本为6.0。的最低要求版本 Xcode…

计算机网络知识点(三)

目录 一、简述TCP连接和关闭的状态转移 二、简述TCP慢启动 三、简述TCP如何保证有序 四、简述TCP常见的拥塞控制算法 五、简述TCP超时重传 一、简述TCP连接和关闭的状态转移 状态转移图 图中上半部分是TCP的三次握手过程的状态变迁&#xff0c;下半部分是TCP四次挥手过程的…

Unity接入PS5手柄和Xbox手柄以及Android平台的(以及不同平台分析)

Unity接入PS5手柄和Xbox手柄以及Android平台的&#xff08;以及不同平台分析&#xff09; 介绍Unity手柄小知识PC端和编辑器上的摇杆事件和滑动事件PS5手柄Xbox手柄北通手柄 安卓环境下&#xff08;安卓手机或者安卓模拟器&#xff09;PS5手柄Xbox手柄北通手柄 总结 介绍 最近…