Chrome源代码分析之Renderer进程初始化
转载来自:
https://blog.csdn.net/namelcx/article/details/8939824
前面已经分析过,一个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);