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);

 

posted @ 2021-11-12 08:56  冰糖葫芦很乖  阅读(490)  评论(0编辑  收藏  举报