Chromium Mojo(IPC)进程通信演示 c++(2)
122版本自带的mojom通信例子associated-interface 仅供学习参考:
codelabs\mojo_examples\02-associated-interface-freezing
一、目录结构如图:
二、interface.mojom接口
1、codelabs\mojo_examples\mojom\interface.mojom
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
module codelabs.mojom;
// Bound in the renderer process; only used in
// `02-associated-interface-freezing` and
// `03-channel-associated-interface-freezing`.
interface ObjectA {
// A sample IPC send to the toy "renderer".
DoA();
};
// Bound in the renderer process.
interface ObjectB {
// A sample IPC send to the toy "renderer".
DoB();
};
// This is a simple generic interface that serves no purpose but to transport a
// message handle from the browser to the renderer. It's just a handle, so it
// requires more type information to actually bind the handle properly on the
// other end (see `Process.GetAssociatedInterface()`).
interface GenericInterface {};
// A process-wide interface that the renderer uses to broker new connections to
// other associated interfaces (see `ObjectA` and `ObjectB` above). This is a
// toy version of `blink.mojom.AssociatedInterfaceProvider`, the mechanism by
// which we bind associated interfaces that span the real browser <=> renderer
// processes in Chromium.
interface Process {
// A sample IPC send to the toy "renderer"; used by the `01-multi-process`
// example.
SayHello();
// Because we're transporting a generic handle, we need more information to
// bind it properly in the toy "renderer" process, which is what the "name"
// argument provides.
GetAssociatedInterface(
string name, pending_associated_receiver<GenericInterface> receiver);
};
自动生成
out\Debug\gen\codelabs\mojo_examples\mojom\interface.mojom.h
out\Debug\gen\codelabs\mojo_examples\mojom\interface.mojom.cc等文件。
2、BUILD.gn
codelabs\mojo_examples\mojom\BUILD.gn
import("//mojo/public/tools/bindings/mojom.gni")
mojom("mojom") {
sources = [ "interface.mojom" ]
public_deps = [
"//ipc:mojom",
"//ipc:mojom_constants",
]
deps = [
"//ipc:mojom",
"//ipc:mojom_constants",
]
# Don't generate a variant sources since we depend on generated internal
# bindings types and we don't generate or build variants of those.
disable_variants = true
}
3、ObjectA和ObjectB接口实现:
codelabs\mojo_examples\mojo_impls.h
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CODELABS_MOJO_EXAMPLES_MOJO_IMPLS_H_
#define CODELABS_MOJO_EXAMPLES_MOJO_IMPLS_H_
#include "base/task/single_thread_task_runner.h"
#include "codelabs/mojo_examples/mojom/interface.mojom.h"
#include "mojo/public/cpp/bindings/associated_receiver.h"
#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
// The classes here are implementations of the mojo interfaces in this
// directory that some of the examples use. They are global instances whose
// receivers get bound to task runners associated with different task queues.
// `ObjectAImpl` is bound to a task queue that is initially frozen, and
// `ObjectBImpl` is bound to the current thread's default task runner,
// associated with an unfrozen task queue.
class ObjectAImpl : public codelabs::mojom::ObjectA {
public:
ObjectAImpl();
~ObjectAImpl() override;
void BindToFrozenTaskRunner(
mojo::PendingAssociatedReceiver<codelabs::mojom::ObjectA>
pending_receiver,
scoped_refptr<base::SingleThreadTaskRunner> freezable_tq_runner);
private:
// codelabs::mojom::ObjectA
void DoA() override;
mojo::AssociatedReceiver<codelabs::mojom::ObjectA> receiver_{this};
};
// The global instance of this class is bound to an unfrozen task queue, but
// doesn't receive any messages until the frozen task queue that manages
// `ObjectAImpl`'s is finally unfroze, after a delay.
class ObjectBImpl : public codelabs::mojom::ObjectB {
public:
ObjectBImpl();
~ObjectBImpl() override;
void Bind(mojo::PendingAssociatedReceiver<codelabs::mojom::ObjectB>
pending_receiver);
private:
// codelabs::mojom::ObjectB
void DoB() override;
mojo::AssociatedReceiver<codelabs::mojom::ObjectB> receiver_{this};
};
#endif // CODELABS_MOJO_EXAMPLES_MOJO_IMPLS_H_
codelabs\mojo_examples\mojo_impls.cc
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "codelabs/mojo_examples/mojo_impls.h"
#include "base/logging.h"
ObjectAImpl::ObjectAImpl() = default;
ObjectAImpl::~ObjectAImpl() = default;
void ObjectAImpl::BindToFrozenTaskRunner(
mojo::PendingAssociatedReceiver<codelabs::mojom::ObjectA> pending_receiver,
scoped_refptr<base::SingleThreadTaskRunner> freezable_tq_runner) {
receiver_.Bind(std::move(pending_receiver), std::move(freezable_tq_runner));
}
void ObjectAImpl::DoA() {
LOG(INFO) << "DoA IPC is being processed!";
}
ObjectBImpl::ObjectBImpl() = default;
ObjectBImpl::~ObjectBImpl() = default;
void ObjectBImpl::Bind(mojo::PendingAssociatedReceiver<codelabs::mojom::ObjectB>
pending_receiver) {
receiver_.Bind(std::move(pending_receiver));
}
void ObjectBImpl::DoB() {
LOG(INFO) << "DoB IPC is being processed!";
}
三、02-mojo-browser.exe
codelabs\mojo_examples\02-associated-interface-freezing\browser.cc
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/command_line.h"
#include "base/logging.h"
#include "base/process/launch.h"
#include "base/run_loop.h"
#include "codelabs/mojo_examples/mojom/interface.mojom.h"
#include "codelabs/mojo_examples/process_bootstrapper.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/remote.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 LaunchAndConnect() {
// Under the hood, this is essentially always an OS pipe (domain socket pair,
// Windows named pipe, mach port, 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("pipe");
base::LaunchOptions options;
// This is the relative path to the mock "renderer process" binary. We pass it
// into `base::LaunchProcess` to run the binary in a new process.
static const base::CommandLine::CharType* argv[] = {
FILE_PATH_LITERAL("./02-mojo-renderer")};
base::CommandLine command_line(1, argv);
channel.PrepareToPassRemoteEndpoint(&options, &command_line);
LOG(INFO) << "Browser: " << command_line.GetCommandLineString();
base::Process child_process = base::LaunchProcess(command_line, options);
channel.RemoteProcessLaunchAttempted();
mojo::OutgoingInvitation::Send(std::move(invitation), child_process.Handle(),
channel.TakeLocalEndpoint());
return pipe;
}
void CreateProcessRemote(mojo::ScopedMessagePipeHandle pipe) {
// An unassociated remote to the toy "renderer" process. We us this to bind
// two associated interface requests.
mojo::PendingRemote<codelabs::mojom::Process> pending_remote(std::move(pipe),
/*version=*/0u);
mojo::Remote<codelabs::mojom::Process> remote(std::move(pending_remote));
remote->SayHello();
// Make an associated interface request for ObjectA and send an IPC.
mojo::PendingAssociatedRemote<codelabs::mojom::GenericInterface>
pending_generic;
remote->GetAssociatedInterface(
"ObjectA", pending_generic.InitWithNewEndpointAndPassReceiver());
mojo::PendingAssociatedRemote<codelabs::mojom::ObjectA> pending_a(
pending_generic.PassHandle(), /*version=*/0u);
mojo::AssociatedRemote<codelabs::mojom::ObjectA> remote_a(
std::move(pending_a));
LOG(INFO) << "Calling ObjectA::DoA() from the browser";
remote_a->DoA();
// Do the same for ObjectB.
mojo::PendingAssociatedRemote<codelabs::mojom::GenericInterface>
pending_generic_2;
remote->GetAssociatedInterface(
"ObjectB", pending_generic_2.InitWithNewEndpointAndPassReceiver());
mojo::PendingAssociatedRemote<codelabs::mojom::ObjectB> pending_b(
pending_generic_2.PassHandle(), /*version=*/0u);
mojo::AssociatedRemote<codelabs::mojom::ObjectB> remote_b(
std::move(pending_b));
LOG(INFO) << "Calling ObjectB::DoB() from the browser";
remote_b->DoB();
}
int main(int argc, char** argv) {
LOG(INFO) << "Browser process starting up";
base::CommandLine::Init(argc, argv);
ProcessBootstrapper bootstrapper;
bootstrapper.InitMainThread(base::MessagePumpType::IO);
bootstrapper.InitMojo(/*as_browser_process=*/true);
mojo::ScopedMessagePipeHandle pipe = LaunchAndConnect();
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(&CreateProcessRemote, std::move(pipe)));
base::RunLoop run_loop;
// Delay shutdown of the browser process for visual effects, as well as to
// ensure the browser process doesn't die while the IPC message is still being
// sent to the target process asynchronously, which would prevent its
// delivery. This delay is an arbitrary 5 seconds, which just needs to be
// longer than the renderer's 3 seconds, which is used to show visually via
// logging, how the ordering of IPCs can be effected by a frozen task queue
// that gets unfrozen 3 seconds later.
base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE,
base::BindOnce(
[](base::OnceClosure quit_closure) {
LOG(INFO) << "'Browser process' shutting down";
std::move(quit_closure).Run();
},
run_loop.QuitClosure()),
base::Seconds(5));
run_loop.Run();
return 0;
}
四、02-mojo-renderer.exe
codelabs\mojo_examples\02-associated-interface-freezing\renderer.cc
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <memory>
#include "base/command_line.h"
#include "base/run_loop.h"
#include "base/task/sequence_manager/task_queue.h"
#include "codelabs/mojo_examples/mojo_impls.h"
#include "codelabs/mojo_examples/mojom/interface.mojom.h"
#include "codelabs/mojo_examples/process_bootstrapper.h"
#include "mojo/public/cpp/bindings/associated_receiver.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/platform/platform_channel.h"
#include "mojo/public/cpp/system/invitation.h"
#include "mojo/public/cpp/system/message_pipe.h"
static ObjectAImpl g_object_a;
static ObjectBImpl g_object_b;
class ProcessImpl : public codelabs::mojom::Process {
public:
ProcessImpl(mojo::PendingReceiver<codelabs::mojom::Process> pending_receiver,
scoped_refptr<base::SingleThreadTaskRunner> freezable_tq_runner) {
receiver_.Bind(std::move(pending_receiver));
freezable_tq_runner_ = std::move(freezable_tq_runner);
}
private:
// codelabs::mojo::Process
void SayHello() override {
LOG(INFO) << "Hello! (invoked in the renderer, from the browser)";
}
void GetAssociatedInterface(
const std::string& name,
mojo::PendingAssociatedReceiver<codelabs::mojom::GenericInterface>
receiver) override {
LOG(INFO) << "Renderer: GetAssociatedInterface() for " << name;
if (name == "ObjectA") {
mojo::PendingAssociatedReceiver<codelabs::mojom::ObjectA> pending_a(
receiver.PassHandle());
g_object_a.BindToFrozenTaskRunner(std::move(pending_a),
std::move(freezable_tq_runner_));
} else if (name == "ObjectB") {
mojo::PendingAssociatedReceiver<codelabs::mojom::ObjectB> pending_b(
receiver.PassHandle());
g_object_b.Bind(std::move(pending_b));
}
}
mojo::Receiver<codelabs::mojom::Process> receiver_{this};
// This is a freezable task runner that only `g_object_a` gets bound to.
scoped_refptr<base::SingleThreadTaskRunner> freezable_tq_runner_;
};
static std::unique_ptr<ProcessImpl> g_process_impl;
class CustomTaskQueue : public base::RefCounted<CustomTaskQueue> {
public:
CustomTaskQueue(base::sequence_manager::SequenceManager& sequence_manager,
const base::sequence_manager::TaskQueue::Spec& spec)
: task_queue_(sequence_manager.CreateTaskQueue(spec)),
voter_(task_queue_->CreateQueueEnabledVoter()) {}
void FreezeTaskQueue() { voter_->SetVoteToEnable(false); }
void UnfreezeTaskQueue() {
LOG(INFO) << "Unfreezing the task queue that `ObjectAImpl` is bound to.";
voter_->SetVoteToEnable(true);
}
const scoped_refptr<base::SingleThreadTaskRunner>& task_runner() const {
return task_queue_->task_runner();
}
private:
~CustomTaskQueue() = default;
friend class base::RefCounted<CustomTaskQueue>;
base::sequence_manager::TaskQueue::Handle task_queue_;
// Used to enable/disable the underlying `TaskQueueImpl`.
std::unique_ptr<base::sequence_manager::TaskQueue::QueueEnabledVoter> voter_;
};
int main(int argc, char** argv) {
base::CommandLine::Init(argc, argv);
LOG(INFO) << "Renderer: "
<< base::CommandLine::ForCurrentProcess()->GetCommandLineString();
// Set up the scheduling infrastructure for this process. It consists of:
// 1.) A SequenceManager that is bound to the current thread (main thread)
// 2.) A default task queue
// 3.) A `CustomTaskQueue` that is easily freezable and unfreezable. This
// part is specific to this example.
ProcessBootstrapper bootstrapper;
bootstrapper.InitMainThread(base::MessagePumpType::IO);
bootstrapper.InitMojo(/*as_browser_process=*/false);
scoped_refptr<CustomTaskQueue> freezable_tq =
base::MakeRefCounted<CustomTaskQueue>(
*bootstrapper.sequence_manager.get(),
base::sequence_manager::TaskQueue::Spec(
base::sequence_manager::QueueName::TEST_TQ));
freezable_tq->FreezeTaskQueue();
// Accept an invitation.
mojo::IncomingInvitation invitation = mojo::IncomingInvitation::Accept(
mojo::PlatformChannel::RecoverPassedEndpointFromCommandLine(
*base::CommandLine::ForCurrentProcess()));
mojo::ScopedMessagePipeHandle pipe = invitation.ExtractMessagePipe("pipe");
base::RunLoop run_loop;
// Post a task that will run in 3 seconds, that will unfreeze the custom task
// queue to which the `codelabs::mojom::ObjectA` object is bound to.
base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE,
base::BindOnce(
[](scoped_refptr<CustomTaskQueue> freezable_tq) {
freezable_tq->UnfreezeTaskQueue();
},
freezable_tq),
base::Seconds(3));
// Create a process-wide receiver that will broker connects to the backing
// `codelabs::mojom::ObjectA` and `codelabs::mojom::ObjectB` implementations.
mojo::PendingReceiver<codelabs::mojom::Process> pending_receiver(
std::move(pipe));
g_process_impl = std::make_unique<ProcessImpl>(std::move(pending_receiver),
freezable_tq->task_runner());
run_loop.Run();
return 0;
}
五、ProcessBootstrapper辅助类
codelabs\mojo_examples\process_bootstrapper.h
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CODELABS_MOJO_EXAMPLES_PROCESS_BOOTSTRAPPER_H_
#define CODELABS_MOJO_EXAMPLES_PROCESS_BOOTSTRAPPER_H_
#include "base/message_loop/message_pump.h"
#include "base/run_loop.h"
#include "base/task/sequence_manager/sequence_manager.h"
#include "base/threading/thread.h"
#include "mojo/core/embedder/embedder.h"
#include "mojo/core/embedder/scoped_ipc_support.h"
class ProcessBootstrapper {
public:
ProcessBootstrapper();
~ProcessBootstrapper();
// This sets up the main thread with a message pump of `type`, and optionally
// a dedicated IO thread if `type` is *not* `base::MessagePumpType::IO`.
void InitMainThread(base::MessagePumpType type) {
// Creates a sequence manager bound to the main thread with a message pump
// of some specified type. The message pump determines exactly what the
// event loop on its thread is capable of (i.e., what *kind* of messages it
// can "pump"). For example, a `DEFAULT` message pump is capable of
// processing simple events, like async timers and posted tasks. The `IO`
// message pump type — which is used in every example in this codelab — is
// capable of asynchronously processing IO over IPC primitives like file
// descriptors, used by Mojo. A thread with *that* kind of message pump is
// required for any process using Mojo for IPC.
std::unique_ptr<base::MessagePump> pump = base::MessagePump::Create(type);
sequence_manager =
base::sequence_manager::CreateSequenceManagerOnCurrentThreadWithPump(
std::move(pump),
base::sequence_manager::SequenceManager::Settings::Builder()
.SetMessagePumpType(type)
.Build());
default_tq = std::make_unique<base::sequence_manager::TaskQueue::Handle>(
sequence_manager->CreateTaskQueue(
base::sequence_manager::TaskQueue::Spec(
base::sequence_manager::QueueName::DEFAULT_TQ)));
sequence_manager->SetDefaultTaskRunner((*default_tq)->task_runner());
if (type == base::MessagePumpType::DEFAULT) {
InitDedicatedIOThread();
}
}
// Must be called after `InitMainThread()`.
void InitMojo(bool as_browser_process) {
CHECK(default_tq) << "Must call `InitMainThread()` before `InitMojo()`";
// Basic Mojo initialization for a new process.
mojo::core::Configuration config;
// For mojo, one process must be the broker process which is responsible for
// trusted cross-process introductions etc. Traditionally this is the
// "browser" process.
config.is_broker_process = as_browser_process;
mojo::core::Init(config);
// The effects of `ScopedIPCSupport` are mostly irrelevant for our simple
// examples, but this class is used to determine how the IPC system shuts
// down. The two shutdown options are "CLEAN" and "FAST", and each of these
// may determine how other processes behave if *this* process has a message
// pipe that is in the middle of proxying messages to another process where
// the other end of the message pipe lives.
//
// In real Chrome, both the browser and renderer processes can safely use
// `FAST` mode, because the side effects of quickly terminating the IPC
// system in the middle of cross-process IPC message proxying is not
// important. See this class's documentation for more information on
// shutdown.
//
// We initialize `ipc_support` with a task runner for whatever thread should
// be the IO thread. This means preferring `io_task_runner` when it is
// non-null, and the default task runner otherwise.
mojo::core::ScopedIPCSupport ipc_support(
io_task_runner ? io_task_runner : (*default_tq)->task_runner(),
mojo::core::ScopedIPCSupport::ShutdownPolicy::FAST);
}
std::unique_ptr<base::sequence_manager::TaskQueue::Handle> default_tq;
std::unique_ptr<base::sequence_manager::SequenceManager> sequence_manager;
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner;
private:
// Note that you cannot call this if you've ever called
// `InitMainThread(base::MessagePumpType::IO)` since that means the main
// thread *itself* the IO thread.
void InitDedicatedIOThread() {
io_thread_ = std::make_unique<base::Thread>("ipc!");
io_thread_->StartWithOptions(
base::Thread::Options(base::MessagePumpType::IO, 0));
io_task_runner = io_thread_->task_runner();
}
std::unique_ptr<base::Thread> io_thread_;
};
ProcessBootstrapper::ProcessBootstrapper() = default;
ProcessBootstrapper::~ProcessBootstrapper() = default;
#endif // CODELABS_MOJO_EXAMPLES_PROCESS_BOOTSTRAPPER_H_
六、codelabs\mojo_examples\BUILD.gn
# Copyright 2020 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
group("codelab_mojo_examples") {
testonly = true
# These examples rely on base::LaunchOptions which do not exist in the iOS
# simulator.
if (!is_ios) {
deps = [
":01-mojo-browser",
":01-mojo-renderer",
":02-mojo-browser",
":02-mojo-renderer",
":03-mojo-browser",
":03-mojo-renderer",
]
}
}
if (!is_ios) {
executable("01-mojo-browser") {
deps = [
"//base",
"//codelabs/mojo_examples/mojom",
"//ipc",
"//mojo/core/embedder",
"//mojo/public/cpp/platform",
"//mojo/public/mojom/base",
]
sources = [
"01-multi-process/browser.cc",
"process_bootstrapper.h",
]
}
executable("01-mojo-renderer") {
deps = [
"//base",
"//codelabs/mojo_examples/mojom",
"//ipc",
"//mojo/core/embedder",
"//mojo/public/cpp/platform",
"//mojo/public/mojom/base",
]
sources = [
"01-multi-process/renderer.cc",
"process_bootstrapper.h",
]
}
executable("02-mojo-browser") {
deps = [
"//base",
"//codelabs/mojo_examples/mojom",
"//ipc",
"//mojo/core/embedder",
"//mojo/public/cpp/platform",
"//mojo/public/mojom/base",
]
sources = [
"02-associated-interface-freezing/browser.cc",
"process_bootstrapper.h",
]
}
executable("02-mojo-renderer") {
deps = [
"//base",
"//codelabs/mojo_examples/mojom",
"//ipc",
"//mojo/core/embedder",
"//mojo/public/cpp/platform",
"//mojo/public/mojom/base",
]
sources = [
"02-associated-interface-freezing/renderer.cc",
"mojo_impls.cc",
"mojo_impls.h",
"process_bootstrapper.h",
]
}
executable("03-mojo-browser") {
deps = [
"//base",
"//codelabs/mojo_examples/mojom",
"//ipc",
"//mojo/core/embedder",
"//mojo/public/cpp/platform",
"//mojo/public/mojom/base",
]
sources = [
"03-channel-associated-interface-freezing/browser.cc",
"process_bootstrapper.h",
]
}
executable("03-mojo-renderer") {
deps = [
"//base",
"//codelabs/mojo_examples/mojom",
"//ipc",
"//mojo/core/embedder",
"//mojo/public/cpp/platform",
"//mojo/public/mojom/base",
]
sources = [
"03-channel-associated-interface-freezing/renderer.cc",
"mojo_impls.cc",
"mojo_impls.h",
"process_bootstrapper.h",
]
}
}
七、编译和调试:
1、gn gen out/debug
自动生成如下文件:
2、 ninja -C out/debug 02-mojo-browser
生成02-mojo-browser.exe
3、ninja -C out/debug 02-mojo-renderer
生成02-mojo-renderer.exe
4、最终生成文件:
八、总结:
1、02-mojo-browser.exe 与02-mojo-renderer.exe通过mojo::OutgoingInvitation模式建立链接。
2、02-mojo-browser.exe通过GetAssociatedInterface接口获取02-mojo-renderer.exe进程对应的
ObjectA和ObjectB接口,并且调用其方法DoA(),DoB()。
更多细节读者自行参考源码。
Chromium Mojo(IPC)进程通信演示 c++(3)-CSDN博客