前面已经分析过,一个RenderProcess与一个主进程中的RenerProcessHost对应。RenderProcess到底在什么时候创建,答案是在RenerProcessHos初始化的时候创建,对应的过程在BrowserRenderProcessHost::Init函数中实现。我们来看看这个函数,函数对应的文件是:
src\chrome\browser\renderer_host\browser_render_process_host.cc
Init开始部分做一些准备工作,比如获取IO线程对象的指针:base::Thread* io_thread = g_browser_process->io_thread();
初始化进程间通信消息过滤对象:
scoped_refptr<ResourceMessageFilter> resource_message_filter(
new ResourceMessageFilter(g_browser_process->resource_dispatcher_host(),
id(),
audio_renderer_host_.get(),
PluginService::GetInstance(),
g_browser_process->print_job_manager(),
profile(),
widget_helper_));
比较重要的是创建IPC对象,首先是初始化一个ChannelID,ChannelID标识了一个属于本RenerProcessHost的唯一的命名管道名,接着用这个ID创建一个IPC对象:
channel_.reset(
new IPC::SyncChannel(channel_id, IPC::Channel::MODE_SERVER, this,
resource_message_filter,
io_thread->message_loop(), true,
g_browser_process->shutdown_event()));
接着到了比较重要的地方,会对当前的运行模式做一个判断,如果是单进程模式,不会创建进程,而是在主进程中创建一个RendererMainThread线程,如果是非按进程模式,会接着创建一个新的RenderProcess。调用的函数是:
static bool run_renderer_in_process() {
return run_renderer_in_process_;
}
这是BrowserRenderProcessHost的父类的静态成员函数,run_renderer_in_process_也是一个静态变量,回顾一下主进程的初始化过程,ChromeMain的第三个参数TCHAR* command_line代表的就是启动chrome.exe的时候传递进来的参数,前面一章已经介绍过,如果command_line是--single-process,那么将运行在单进程模式下,那么,会调用下面的代码设置run_renderer_in_process_的值:
if (single_process)
RenderProcessHost::set_run_renderer_in_process(true);
再回到刚才的代码,如果是单进程模型:
in_process_renderer_.reset(new RendererMainThread(channel_id));
base::Thread::Options options;
#if !defined(TOOLKIT_USES_GTK)
// In-process plugins require this to be a UI message loop.
options.message_loop_type = MessageLoop::TYPE_UI;
#else
// We can't have multiple UI loops on GTK, so we don't support
// in-process plugins.
options.message_loop_type = MessageLoop::TYPE_DEFAULT;
#endif
in_process_renderer_->StartWithOptions(options);
OnProcessLaunched(); // Fake a callback that the process is ready.
首先创建RendererMainThread的对象,接着设置线程的类型为TYPE_UI,也就是说,这个线程是一个能接收处理windows系统消息的线程,这与多进程模型是完全不同的。
最后调用n_process_renderer_->StartWithOptions(options);启动这个线程的消息循环。
如果是标准的多进程模型:
CommandLine* cmd_line = new CommandLine(renderer_path);
if (!renderer_prefix.empty())
cmd_line->PrependWrapper(renderer_prefix);
AppendRendererCommandLine(cmd_line);
cmd_line->AppendSwitchASCII(switches::kProcessChannelID, channel_id);
// Spawn the child process asynchronously to avoid blocking the UI thread.
// As long as there's no renderer prefix, we can use the zygote process
// at this stage.
child_process_.reset(new ChildProcessLauncher(
#if defined(OS_WIN)
FilePath(),
#elif defined(POSIX)
renderer_prefix.empty(),
base::environment_vector(),
channel_->GetClientFileDescriptor(),
#endif
cmd_line,
this));
fast_shutdown_started_ = false;
上面做的主要工作就是创建一个新的RenderProcess,并且通过cmd_line把ChannelID传递给这个新的进程,这样RenderProcess和RenerProcessHost才能建立一对一的关系,完成正常的进程间通信的任务。
接着再看看创建进程的具体实现,代码位于:
src\chrome\browser\child_process_launcher.cc
代码如下:
ChildProcessLauncher::ChildProcessLauncher(
#if defined(OS_WIN)
const FilePath& exposed_dir,
#elif defined(OS_POSIX)
bool use_zygote,
const base::environment_vector& environ,
int ipcfd,
#endif
CommandLine* cmd_line,
Client* client) {
context_ = new Context();
context_->Launch(
#if defined(OS_WIN)
exposed_dir,
#elif defined(OS_POSIX)
use_zygote,
environ,
ipcfd,
#endif
cmd_line,
client);
}
重点是context_->Launch,context_->Launch的唯一工作就是向BrowserThread线程发起一个Task,执行LaunchInternal函数,该函数调用handle = sandbox::StartProcessWithAccess(cmd_line, exposed_dir);
StartProcessWithAccess先通过传递的cmd_line确定进程的类型,枚举类型定义在ChildProcessInfo类里面,如下:
enum ProcessType {
UNKNOWN_PROCESS = 1,
BROWSER_PROCESS,
RENDER_PROCESS,
PLUGIN_PROCESS,
WORKER_PROCESS,
NACL_LOADER_PROCESS,
UTILITY_PROCESS,
PROFILE_IMPORT_PROCESS,
ZYGOTE_PROCESS,
SANDBOX_HELPER_PROCESS,
NACL_BROKER_PROCESS,
GPU_PROCESS
};
经过一系列判断,如果非sandbox进程,那么调用: base::LaunchApp(*cmd_line, false, false, &process);
如果是sandbox进程,调用 result = g_broker_services->SpawnTarget(
cmd_line->GetProgram().value().c_str(),
cmd_line->command_line_string().c_str(),
policy, &target);