No one knows the Mojo better than its designer and thus the best introduction is Intro to Mojo & Services. Here I provide a brief overview for the Mojo.
Mojo is an Inter-Process Communication (IPC) framework. Before Mojo comes out, Chromium also had an IPC framework implemented in C++ macros, called legacy IPC. To reduce redundant code and decomposing services, Chromium refactors the underlying implementation of legacy IPC and creates Mojo. Currently, most IPC in Chromium is implemented with Mojo and the underlying implementation of legacy IPC has been replaced with Mojo. Below is a figure from Mojo documents showing different layers of Mojo.
Mojo core is responsible for the really core implementation of IPC. It focuses on building the IPC network and routing IPC messages to the right place. Mojo System API is a simple wrapper for Mojo core while the real platform-specific logic, like Windows named pipes and Unix sockets, is implemented in C++ System in the figure above. The C++ bindings is usually what Chromium really relies on and provides most high-level abstractions.
The way Mojo works is pretty similar to protobuf. Firstly, the user defines various interfaces in a
mojom file, each of which represents a function which can be called by a remote endpoint. During the process of compiling, the
mojom files will be compiled to different bindings. For example, for C++ code, a
example.mojom file will generate a
example.mojom.h file which other source files can include.
The implementation of Mojo is really simple and straightforward. Enter
mojo/public/tools/bindings/generators/cpp_templates directory and all secrets are revealed.
Yes, Mojo creates a set of source templates for different language bindings. After parsing
mojom file, it simply renders the templates with data retrieved from AST. Thus, a good way to set instrumentation is to modify
A Mojo network is made up of many
Nodes, which means a process running Mojo. It works pretty like a TCP/IP network but one key difference is that there is one broker process in a Mojo network. The broker process (the browser process in Chromium) is usually a full-privileged process and responsible for assisting communication between nodes.
Next I will take the Chromium for an example to introduce some details in Mojo.
The very first thing for a new Mojo node is to join the Mojo network. In Chromium case, every child process is spawned from the browser process (the broker in Mojo) and a bootstrap channel will be created in advance. To establish the connection with new child process, the browser process has to pass the handle of the bootstrap channel to it. The methods vary on different platforms.
From the code snippet above, it is clear that the initial channel handle is retrieved from command line arguments on Windows and Fuchsia. On macOS, it is passed through Mach. On Linux, since the child process is forked from its parent process, the handle is simply retrieved from global descriptors.
Once the child process has been launched, the browser process sends an invitation to the child process.
After the child process accepts the invitation, the platform channel is established. Then, the browser process sends a
BrokerClient message to inform the child process the name of the broker process. Note that the inviter process may be not the broker process in other cases. After receiving the message, the child process updates the broker name and peers the inviter with previous bootstrap channel reused.
void NodeController::OnAcceptBrokerClient(const ports::NodeName& from_node,
At this time, the child process has succeeded in joining the Mojo network.
As the title suggests, Mojo is more of a protocol. IPC is not something new, but what makes Mojo innovative is that it defines an IPC protocol capable of exchanging information and resources across the process boundary.
The first layer of a Mojo IPC message is
Message. Below is the definition of its header.
To illustrate it clearly, below is an ASCII figure.
Note that only on Windows and macOS, handles are serialized into the extra headers.
The second layer is
ChannelMessage, which is encoded in the
payload field of
And corresponding ASCII figure.
Note that the
data field in
ChannelMessage is not fixed-length. It depends on the type of
ChannelMessage types is
EVENT_MESSAGE. In fact,
EventMessage is a large class of messages. The definition of its header is as follows.
and corresponding ASCII figure.
Specifically, for a
As a type of
UserMessage also has its header. Contrary to previous design, the
UserMessage is attached after the whole
and corresponding ASCII figure.
For every dispatcher, a
DispatcherData will be appended to
In my view, Mojo is more like a protocol, not a framework. The good message layering design decomposes different functions and extends the flexibility of Mojo itself.