本文介绍browser 进程和 render 进程通信通道的建立, browser 进程与其他子进程, 比如 GPU 进程,Plugin 进程间通信通道的建立是类似的.
Chromium ipc 正在转向Mojom. Mojom 将会在后续的博文中介绍. 本文的介绍是建立在Mojom 背景知识之上的. 另外, 有关render process 进程的启动过程,也请读者先参考罗升阳分享的文章《Chromium的Render进程启动过程分析 》 本文关注的是进程间通道建立的细节.
在 browser 进程创建新的 进程前,会先 new RenderProcessHostImpl 的一个实体, 在RenderProcessHostImpl的构造函数里会调用RenderProcessHostImpl::InitializeChannelProxy(). 在 RenderProcessHostImpl::InitializeChannelProxy()中有为进程间通信做准备工作.
void RenderProcessHostImpl::InitializeChannelProxy() {
// Generate a token used to identify the new child process.
child_token_ = mojo::edk::GenerateRandomToken();
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner =
BrowserThread::GetTaskRunnerForThread(BrowserThread::IO);
// Acquire a Connector which will route connections to a new instance of the
// renderer service.
service_manager::Connector* connector =
BrowserContext::GetConnectorFor(browser_context_);
if (!connector) {
// Note that some embedders (e.g. Android WebView) may not initialize a
// Connector per BrowserContext. In those cases we fall back to the
// browser-wide Connector.
if (!ServiceManagerConnection::GetForProcess()) {
// Additionally, some test code may not initialize the process-wide
// ServiceManagerConnection prior to this point. This class of test code
// doesn't care about render processes, so we can initialize a dummy
// connection.
service_manager::mojom::ServiceRequest request(&test_service_);
ServiceManagerConnection::SetForProcess(ServiceManagerConnection::Create(
std::move(request), io_task_runner));
}
connector = ServiceManagerConnection::GetForProcess()->GetConnector();
}
// Establish a ServiceManager connection for the new render service instance.
child_connection_.reset(new ChildConnection(
mojom::kRendererServiceName,
base::StringPrintf("%d_%d", id_, instance_id_++), child_token_, connector,
io_task_runner));
// Send an interface request to bootstrap the IPC::Channel. Note that this
// request will happily sit on the pipe until the process is launched and
// connected to the ServiceManager. We take the other end immediately and
// plug it into a new ChannelProxy.
IPC::mojom::ChannelBootstrapPtr bootstrap;
GetRemoteInterfaces()->GetInterface(&bootstrap);
std::unique_ptr<IPC::ChannelFactory> channel_factory =
IPC::ChannelMojo::CreateServerFactory(
bootstrap.PassInterface().PassHandle(), io_task_runner);
ResetChannelProxy();
// Do NOT expand ifdef or run time condition checks here! Synchronous
// IPCs from browser process are banned. It is only narrowly allowed
// for Android WebView to maintain backward compatibility.
// See crbug.com/526842 for details.
#if defined(OS_ANDROID)
if (GetContentClient()->UsingSynchronousCompositing()) {
channel_ = IPC::SyncChannel::Create(
this, io_task_runner.get(), &never_signaled_);
}
#endif // OS_ANDROID
if (!channel_)
channel_.reset(new IPC::ChannelProxy(this, io_task_runner.get()));
channel_->Init(std::move(channel_factory), true /* create_pipe_now */);
// See OnProcessLaunched() for some additional details of this somewhat
// surprising behavior.
channel_->GetRemoteAssociatedInterface(&remote_route_provider_);
channel_->GetRemoteAssociatedInterface(&renderer_interface_);
// We start the Channel in a paused state. It will be briefly unpaused again
// in Init() if applicable, before process launch is initiated.
channel_->Pause();
}
这段code 在src/content/browser/render_host/render_process_host_impl.cc中. 请大家先注意child_token_, connector, ChildConnection,channel_factory 几个关键点.
下面一一探究.
child_token_ 是一串随机字串, 是为后面Render 进程与Browser 进程握手时使用的标志符. 它被传递给了ChildConnection. 后文还会有详细介绍.
connector service_manager::Connector* connector = BrowserContext::GetConnectorFor(browser_context_);
这里的connector 在browser main process 启动时已经创建好了. 看下面的code:
// static
service_manager::Connector* BrowserContext::GetConnectorFor(
BrowserContext* browser_context) {
ServiceManagerConnection* connection =
GetServiceManagerConnectionFor(browser_context);
return connection ? connection->GetConnector() : nullptr;
}
// static
ServiceManagerConnection* BrowserContext::GetServiceManagerConnectionFor(
BrowserContext* browser_context) {
BrowserContextServiceManagerConnectionHolder* connection_holder =
static_cast<BrowserContextServiceManagerConnectionHolder*>(
browser_context->GetUserData(kServiceManagerConnection));
return connection_holder ? connection_holder->service_manager_connection()
: nullptr;
}
// static
void BrowserContext::Initialize(
BrowserContext* browser_context,
const base::FilePath& path) {
std::string new_id;
if (GetContentClient() && GetContentClient()->browser()) {
new_id = GetContentClient()->browser()->GetServiceUserIdForBrowserContext(
browser_context);
} else {
// Some test scenarios initialize a BrowserContext without a content client.
new_id = base::GenerateGUID();
}
ServiceUserIdHolder* holder = static_cast<ServiceUserIdHolder*>(
browser_context->GetUserData(kServiceUserId));
if (holder)
file::ForgetServiceUserIdUserDirAssociation(holder->user_id());
file::AssociateServiceUserIdWithUserDir(new_id, path);
RemoveBrowserContextFromUserIdMap(browser_context);
g_user_id_to_context.Get()[new_id] = browser_context;
browser_context->SetUserData(kServiceUserId,
new ServiceUserIdHolder(new_id));
browser_context->SetUserData(kMojoWasInitialized,
new base::SupportsUserData::Data);
ServiceManagerConnection* service_manager_connection =
ServiceManagerConnection::GetForProcess();
if (service_manager_connection && base::ThreadTaskRunnerHandle::IsSet()) {
// NOTE: Many unit tests create a TestBrowserContext without initializing
// Mojo or the global service manager connection.
service_manager::mojom::ServicePtr service;
service_manager::mojom::ServiceRequest service_request(&service);
service_manager::mojom::PIDReceiverPtr pid_receiver;
service_manager::Identity identity(mojom::kBrowserServiceName, new_id);
service_manager_connection->GetConnector()->StartService(
identity, std::move(service), mojo::MakeRequest(&pid_receiver));
pid_receiver->SetPID(b