前面已經分析過,一個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);