WebKit之Chromium的Render进程分析

3 篇文章 0 订阅

配置多进程的情况下,Chromium的网页渲染和JS执行在一个单独的进程中进行。这个进程称为Render进程,由Browser进程启动。在Android平台中,Browser进程就是Android应用程序的主进程,而Render进程就是Android应用程序的Service进程,它们通过UNIX Socket进行通信。本文就详细分析Chromium的Browser进程启动Render进程的过程。

老罗的新浪微博:http://weibo.com/shengyangluo,欢迎关注!

       Render进程启动完成之后,将与Browser进程建立以下的IPC通道,如图1所示:

图1 Browser进程与Render进程的IPC通信过程

       在Browser进程中,一个RenderProcessHost对象用来描述它所启动的一个Render进程,而一个RenderViewHost对象用来描述运行在一个Render进程中的一个网页,我们可以将它理解为浏览器中的一个TAB。这两个对象在Render进程中都有一个对等体,它们分别是一个RenderProcess对象和一个RenderView对象。这里说的对等体,就是它们是Browser进程和Render进程进行IPC的两个端点,类似于TCP/IP网络堆栈中的层对层通信。例如,RenderViewHost和RenderView之间的IPC通信,就代表了Browser进程请求Render进程加载、更新和渲染一个网页。

       RenderViewHost和RenderView之间的IPC通信,实际上是通过一个UNIX Socket进行的。这个UNIX Socket的两端分别被封装为两个Channel对象,分别运行在Browser进程和Render进程各自的IO线程中。这样RenderViewHost和RenderView之间的IPC通信就要通过上述的两个Channel对象进行。

       在Browser进程中,由于RenderViewHost对象运行在主线程中,因此当它需要请求运行在IO线程中的Channel对象执行一次IPC时,就要通过IO线程的消息循环进行。这符合我们在前面Chromium多线程模型设计和实现分析一文中提到的Chromium的多线程设计哲学:每一个对象都只运行在一个线程中,对象之间需要通信时就通过消息循环进行。同样,在Render进程中,由于RenderView对象运行在Render线程中,因此当Render进程的Channel对象接收一个来自Browser进程的RenderViewHost对象的IPC消息时,需要通过Render线程的消息循环将IPC消息转发给RenderView进行处理。从RenderView对象到RenderViewHost对象的通信过程也是类似的。

       我们分析Render进程的启动过程,目的就是为了能够理解Browser进程和Render进程是如何建立IPC通道的,因为以后Browser进程与Render进程的交互和协作,都是通过这个IPC通道进行的。为此,我们在分析Render进程的启动过程中,将着重分析图1涉及到的各个对象的初始过程。

       我们注意到,运行在Browser进程中的通信对象是以Host结尾的,而在运行在Render进程中的对等通信对象,则是没有Host结尾的,因此当我们Chromium的源代码中看到一个对象的类型时,就可以推断出该对象运行在哪个进程中。

       事实上,RenderProcessHost、RenderViewHost、RenderProcess和RenderView仅仅是定义了一个抽象接口,真正用来执行IPC通信的对象,是实现了上述抽象接口的一个实现者对象,这些实现者对象的类型以Impl结尾,因此,RenderProcessHost、RenderViewHost、RenderProcess和RenderView对应的实现者对象的类型就分别为RenderProcessHostImpl、RenderViewHostImpl、RenderProcessImpl和RenderViewImpl。

       为了更好地理解Render进程的启动过程,我们有必要了解上述Impl对象的类关系图。

       RenderViewHostImpl对象的类关系图如下所示:

图2 RenderViewHostImpl类关系图

       RenderViewHostImpl类多重继承了RenderViewHost类和RenderWidgetHostImpl类,后面这两个类又有一个共同的虚基类RenderWidgetHost,该虚基类又实现了一个Sender接口,该接口定义了一个重要的成员函数Send,用来执行IPC通信。

       RenderWidgetHostImpl类还实现了一个Listener接口,该接口定义了两个重要的成员函数OnMessageReceived和OnChannelConnected。前者用来接收IPC消息并且进行分发,后者用来在IPC通道建立时执行一些初始化工作。

       实际上,当RenderViewHostImpl类需要发起一次IPC时,它是通过父类RenderWidgetHostImpl的成员变量process_指向的一个RenderProcessHost接口进行的。该RenderProcessHost接口指向的实际上是一个RenderProcessHostImpl对象,它的类关系图如图3所示:

图3 RenderProcessHostImpl类关系图

       RenderProcessHostImpl类实现了RenderProcessHost接口,后者又多重继承了Sender和Listener类。

       RenderProcessHostImpl类有一个成员变量channel_,它指向了一个ChannelProxy对象。ChannelProxy类实现了Sender接口,RenderProcessHostImpl类就是通过它来发送IPC消息的。

       ChannelProxy类有一个成员变量context_,它指向了一个ChannelProxy::Context对象。ChannelProxy::Context类实现了Listener接口,因此它可以用来接收IPC消息。ChannelProxy类就是通过ChannelProxy::Context类来发送和接收IPC消息的。

       ChannelProxy::Context类有一个类型为Channel的成员变量channel_,它指向的实际上是一个ChannelPosix对象。ChannelPosix类继承了Channel类,后者又实现了Sender接口。ChannelProxy::Context类就是通过ChannelPosix类发送IPC消息的。

       绕了一圈,总结来说,就是RenderProcessHostImpl类是分别通过ChannelPosix类和ChannelProxy::Context类来发送和接收IPC消息的。

       上面分析的RenderViewHostImpl对象和RenderProcessHostImpl对象都是运行在Browser进程的,接下来要分析的RenderViewImpl类和RenderProcessImpl类是运行在Render进程的。

        RenderViewImpl对象的类关系图如下所示:

图4 RenderViewImpl类关系图

       RenderViewImpl类多重继承了RenderView类和RenderWidget类。RenderView类实现了Sender接口。RenderWidget类也实现了Sender接口,同时也实现了Listener接口,因此它可以用来发送和接收IPC消息。

       RenderWidget类实现了接口Sender的成员函数Send,RenderViewImpl类就是通过它来发送IPC消息的。RenderWidget类的成员函数Send又是通过一个用来描述Render线程的RenderThreadImpl对象来发送IPC类的。这个RenderThreadImpl对象可以通过调用RenderThread类的静态成员函数Get获得。

       RenderThreadImpl对象的类关系图如下所示:

图5 RenderThreadImpl类关系图

       RenderThreadImpl类多重继承了RenderThread类和ChildThread类。RenderThread类实现了Sender接口。ChildThread类也实现Sender接口,同时也实现了Listener接口,因此它可以用来发送和接收IPC消息。

       ChildThread类有一个成员变量channel_,它指向了一个SyncChannel对象。SyncChannel类继承了上面提到的ChannelProxy类,因此,ChildThread类通过其成员变量channel_指向的SyncChannel对象可以发送IPC消息。

       从上面的分析又可以知道,ChannelProxy类最终是通过ChannelPosix类发送IPC消息的,因此总结来说,就是RenderThreadImpl是通过ChannelPosix类发送IPC消息的。

       接下来我们再来看RenderProcessImpl对象的类关系图,如下所示:

图6  RenderProcessImpl类关系图

       RenderProcessImpl类继承了RenderProcess类,RenderProcess类又继承了ChildProcess类。ChildProcess类有一个成员变量io_thread_,它指向了一个Thread对象。该Thread对象描述的就是Render进程的IO线程。

       有了上面的基础知识之后,接下来我们开始分析Render进程的启动过程。我们将Render进程的启动过程划分为两部分。第一部分是在Browser进程中执行的,它主要负责创建一个UNIX Socket,并且将该UNIX Socket的Client端描述符传递给接下来要创建的Render进程。第二部分是在Render进程中执行的,它负责执行一系列的初始化工作,其中之一就是将Browser进程传递过来的UNIX Socket的Client端描述符封装在一个Channel对象中,以便以后可以通过它来和Browser进程执行IPC。

      Render进程启动过程的第一部分子过程如下所示:

图7 Render进程启动的第一部分子过程

      图7列出的仅仅是一些核心过程,接下来我们通过代码来分析这些核心过程。

      我们首先了解什么情况下Browser进程会启动一个Render进程。当我们在Chromium的地址栏输入一个网址,然后进行加载的时候,Browser进程经过判断,发现需要在一个新的Render进程中渲染该网址的内容时,就会创建一个RenderViewHostImpl对象,并且调用它的成员函数CreateRenderView触发启动一个新的Render进程。后面我们分析WebView加载一个URL的时候,就会看到触发创建RenderViewHostImpl对象的流程。

      RenderViewHostImpl对象的创建过程,即RenderViewHostImpl类的构造函数的实现如下所示:

 

[cpp] view plaincopy

  1. RenderViewHostImpl::RenderViewHostImpl(  
  2.     SiteInstance* instance,  
  3.     RenderViewHostDelegate* delegate,  
  4.     RenderWidgetHostDelegate* widget_delegate,  
  5.     int routing_id,  
  6.     int main_frame_routing_id,  
  7.     bool swapped_out,  
  8.     bool hidden)  
  9.     : RenderWidgetHostImpl(widget_delegate,  
  10.                            instance->GetProcess(),  
  11.                            routing_id,  
  12.                            hidden),  
  13.       ...... {  
  14.   
  15.   ......  
  16. }  

      这个函数定义在文件external/chromium_org/content/browser/renderer_host/render_view_host_impl.cc中。

 

      这里我们主要关注类型为SiteInstance的参数instance,它指向的实际上是一个SiteInstanceImpl对象,用来描述Chromium当前加载的一个网站实例。RenderViewHostImpl类的构造函数调用该SiteInstanceImpl对象的成员函数GetProcess获得一个RenderProcessHostImpl对象,如下所示:

 

[cpp] view plaincopy

  1. RenderProcessHost* SiteInstanceImpl::GetProcess() {  
  2.   ......  
  3.   
  4.   // Create a new process if ours went away or was reused.  
  5.   if (!process_) {  
  6.     BrowserContext* browser_context = browsing_instance_->browser_context();  
  7.   
  8.     // If we should use process-per-site mode (either in general or for the  
  9.     // given site), then look for an existing RenderProcessHost for the site.  
  10.     bool use_process_per_site = has_site_ &&  
  11.         RenderProcessHost::ShouldUseProcessPerSite(browser_context, site_);  
  12.     if (use_process_per_site) {  
  13.       process_ = RenderProcessHostImpl::GetProcessHostForSite(browser_context,  
  14.                                                               site_);  
  15.     }  
  16.   
  17.     // If not (or if none found), see if we should reuse an existing process.  
  18.     if (!process_ && RenderProcessHostImpl::ShouldTryToUseExistingProcessHost(  
  19.             browser_context, site_)) {  
  20.       process_ = RenderProcessHostImpl::GetExistingProcessHost(browser_context,  
  21.                                                                site_);  
  22.     }  
  23.   
  24.     // Otherwise (or if that fails), create a new one.  
  25.     if (!process_) {  
  26.       if (g_render_process_host_factory_) {  
  27.         process_ = g_render_process_host_factory_->CreateRenderProcessHost(  
  28.             browser_context, this);  
  29.       } else {  
  30.         StoragePartitionImpl* partition =  
  31.             static_cast<StoragePartitionImpl*>(  
  32.                 BrowserContext::GetStoragePartition(browser_context, this));  
  33.         process_ = new RenderProcessHostImpl(browser_context,  
  34.                                              partition,  
  35.                                              site_.SchemeIs(kGuestScheme));  
  36.       }  
  37.     }  
  38.   
  39.     ......  
  40.   }  
  41.   ......  
  42.   
  43.   return process_;  
  44. }  

       这个函数定义在文件external/chromium_org/content/browser/site_instance_impl.cc中。

 

       SiteInstanceImpl对象的成员变量process_是一个RenderProcessHost指针,当它的值等于NULL的时候,就表示Chromium还没有为当前正在处理的一个SiteInstanceImpl对象创建过Render进程,这时候就需要创建一个RenderProcessHostImpl对象,并且保存在成员变量process_中,以及返回给调用者,以便调用者接下来可以通过它启动一个Render进程。另一方面,如果SiteInstanceImpl对象的成员变量process_已经指向了一个RenderProcessHostImpl对象,那么就直接将该RenderProcessHostImpl对象返回给调用者即可。

       注意上述RenderProcessHostImpl对象的创建过程:

       1. 如果Chromium启动时,指定了同一个网站的所有网页都在同一个Render进程中加载,即本地变量use_process_per_site的值等于true,那么这时候SiteInstanceImpl类的成员函数GetProcess就会先调用RenderProcessHostImpl类的静态函数GetProcessHostForSite检查之前是否已经为当前正在处理的SiteInstanceImpl对象描述的网站创建过Render进程。如果已经创建过,那么就可以获得一个对应的RenderProcessHostImpl对象。

       2. 如果按照上面的方法找不到一个相应的RenderProcessHostImpl对象,本来就应该要创建一个新的Render进程了,也就是要创建一个新的RenderProcessHostImpl对象了。但是由于当前创建的Render进程已经超出预设的最大数量了,这时候就要复用前面已经启动的Render进程,即使这个Render进程加载的是另一个网站的内容。

       3. 如果通过前面两步仍然找不到一个对应的RenderProcessHostImpl对象,这时候就真的是需要创建一个RenderProcessHostImpl对象了。取决于SiteInstanceImpl类的静态成员变量g_render_process_host_factory_是否被设置,创建一个新的RenderProcessHostImpl对象的方式有所不同。如果该静态成员变量被设置了指向一个RenderProcessHostFactory对象,那么就调用该RenderProcessHostFactory对象的成员函数CreateRenderProcessHost创建一个从RenderProcessHost类继承下来的子类对象。否则的话,就直接创建一个RenderProcessHostImpl对象。

       这一步执行完成后,回到RenderViewHostImpl类的构造函数中,从这里返回的RenderProcessHostImpl对象用来初始化RenderViewHostImpl类的父类RenderWidgetHostImpl,如下所示:

 

[cpp] view plaincopy

  1. RenderWidgetHostImpl::RenderWidgetHostImpl(RenderWidgetHostDelegate* delegate,  
  2.                                            RenderProcessHost* process,  
  3.                                            int routing_id,  
  4.                                            bool hidden)  
  5.     : ......,  
  6.       process_(process),  
  7.       ...... {  
  8.   ......  
  9. }  

      这个函数定义在文件external/chromium_org/content/browser/renderer_host/render_widget_host_impl.cc中。

 

      参数process指向的RenderProcessHostImpl对象保存在RenderWidgetHostImpl类的成员变量process_中,以后就可以通过RenderWidgetHostImpl类的成员函数GetProcess获得该RenderProcessHostImpl对象,如下所示:

 

[cpp] view plaincopy

  1. RenderProcessHost* RenderWidgetHostImpl::GetProcess() const {  
  2.   return process_;  
  3. }  

      这个函数定义在文件external/chromium_org/content/browser/renderer_host/render_widget_host_impl.cc中。

 

      有了RenderProcessHostImpl之后,接下来我们就开始分析RenderViewHostImpl类的成员函数CreateRenderView创建一个新的Render进程的过程了,如下所示:

 

[cpp] view plaincopy

  1. bool RenderViewHostImpl::CreateRenderView(  
  2.     const base::string16& frame_name,  
  3.     int opener_route_id,  
  4.     int proxy_route_id,  
  5.     int32 max_page_id,  
  6.     bool window_was_created_with_opener) {  
  7.   ......  
  8.   
  9.   if (!GetProcess()->Init())  
  10.     return false;  
  11.   
  12.   ......  
  13.   
  14. }  

       这个函数定义在文件external/chromium_org/content/browser/renderer_host/render_view_host_impl.cc中。

       RenderViewHostImpl类的成员函数CreateRenderView首先调用从父类RenderWidgetHostImpl继承下来的成员函数GetProcess获得一个RenderProcessHostImpl对象,接着再调用该RenderProcessHostImpl对象的成员函数Init检查是否需要为当前加载的网页创建一个新的Render进程。

       RenderProcessHostImpl类的成员函数Init的实现如下所示:

 

[cpp] view plaincopy

  1. bool RenderProcessHostImpl::Init() {  
  2.   // calling Init() more than once does nothing, this makes it more convenient  
  3.   // for the view host which may not be sure in some cases  
  4.   if (channel_)  
  5.     return true;  
  6.   
  7.   ......  
  8.   
  9.   // Setup the IPC channel.  
  10.   const std::string channel_id =  
  11.       IPC::Channel::GenerateVerifiedChannelID(std::string());  
  12.   channel_ = IPC::ChannelProxy::Create(  
  13.       channel_id,  
  14.       IPC::Channel::MODE_SERVER,  
  15.       this,  
  16.       BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO).get());  
  17.   
  18.   ......  
  19.   
  20.   CreateMessageFilters();  
  21.   
  22.   ......  
  23.   
  24.   if (run_renderer_in_process()) {  
  25.     ......  
  26.     in_process_renderer_.reset(g_renderer_main_thread_factory(channel_id));  
  27.   
  28.     base::Thread::Options options;  
  29.     ......  
  30.     options.message_loop_type = base::MessageLoop::TYPE_DEFAULT;  
  31.       
  32.     in_process_renderer_->StartWithOptions(options);  
  33.   
  34.     g_in_process_thread = in_process_renderer_->message_loop();  
  35.   
  36.     ......  
  37.   } else {  
  38.     ......  
  39.   
  40.     CommandLine* cmd_line = new CommandLine(renderer_path);  
  41.     ......  
  42.     AppendRendererCommandLine(cmd_line);  
  43.     cmd_line->AppendSwitchASCII(switches::kProcessChannelID, channel_id);  
  44.   
  45.     ......  
  46.   
  47.     child_process_launcher_.reset(new ChildProcessLauncher(  
  48.         new RendererSandboxedProcessLauncherDelegate(channel_.get()),  
  49.         cmd_line,  
  50.         GetID(),  
  51.         this));  
  52.   
  53.     ......  
  54.   }  
  55.   
  56.   return true;  
  57. }  

       这个函数定义在文件external/chromium_org/content/browser/renderer_host/render_process_host_impl.cc中。

 

       RenderProcessHostImpl类有一个类型为scoped_ptr<IPC::ChannelProxy>成员变量channel_,当它引用了一个IPC::ChannelProxy对象的时候,就表明已经为当前要加载的网而创建过Render进程了,因此在这种情况下,就无需要往前执行了。

       我们假设到目前为止,还没有为当前要加载的网页创建过Render进程。接下来RenderProcessHostImpl类的成员函数Init就会做以下四件事情:

       1. 先调用IPC::Channel类的静态成员函数GenerateVerifiedChannelID生成一个接下来用于创建UNIX Socket的名字,接着再以该名字为参数,调用IPC::ChannelProxy类的静态成员函数Create创建一个用于执行IPC的Channel,该Channel就保存在RenderProcessHostImpl类的成员变量channel_中。

       2. 调用RenderProcessHostImpl类的成员函数CreateMessageFilters创建一系列的Message Filter,用来过滤IPC消息。

       3. 如果所有网页都在Browser进程中加载,即不单独创建Render进程来加载网页,那么这时候调用父类RenderProcessHost的静态成员函数run_renderer_in_process的返回值就等于true。在这种情况下,就会通过在本进程(即Browser进程)创建一个新的线程来渲染网页。这个线程由RenderProcessHostImpl类的静态成员变量g_renderer_main_thread_factory描述的一个函数创建,它的类型为InProcessRendererThread。InProcessRendererThread类继承了base::Thread类,从前面Chromium多线程模型设计和实现分析一文可以知道,当调用它的成员函数StartWithOptions的时候,新的线程就会运行起来。这时候如果我们再调用它的成员函数message_loop,就可以获得它的Message Loop。有了这个Message Loop之后,以后就可以向它发送消息了。

       4. 如果网页要单独的Render进程中加载,那么调用创建一个命令行,并且以该命令行以及前面创建的IPC::ChannelProxy对象为参数,创建一个ChildProcessLauncher对象,而该ChildProcessLauncher对象在创建的过程,就会启动一个新的Render进程。

       接下来,我们主要分析第1、3和4件事情,第2件事情在接下来的一篇文章中分析IPC消息分发机制时再分析。

       第一件事情涉及到IPC::Channel类的静态成员函数GenerateVerifiedChannelID和IPC::ChannelProxy类的静态成员函数Create。

       IPC::Channel类的静态成员函数GenerateVerifiedChannelID的实现如下所示:

 

[cpp] view plaincopy

  1. std::string Channel::GenerateVerifiedChannelID(const std::string& prefix) {  
  2.   // A random name is sufficient validation on posix systems, so we don't need  
  3.   // an additional shared secret.  
  4.   
  5.   std::string id = prefix;  
  6.   if (!id.empty())  
  7.     id.append(".");  
  8.   
  9.   return id.append(GenerateUniqueRandomChannelID());  
  10. }  

       这个函数定义在文件external/chromium_org/ipc/ipc_channel_posix.cc中。

 

       IPC::Channel类的静态成员函数GenerateVerifiedChannelID实际上是调用另外一个静态成员函数GenerateUniqueRandomChannelID生成一个唯一的随机名字,后者的实现如下所示:

 

[cpp] view plaincopy

  1. base::StaticAtomicSequenceNumber g_last_id;  
  2.   
  3. ......  
  4.   
  5. std::string Channel::GenerateUniqueRandomChannelID() {  
  6.   ......  
  7.   
  8.   int process_id = base::GetCurrentProcId();  
  9.   return base::StringPrintf("%d.%u.%d",  
  10.       process_id,  
  11.       g_last_id.GetNext(),  
  12.       base::RandInt(0, std::numeric_limits<int32>::max()));  
  13. }  

      这个函数定义在文件external/chromium_org/ipc/ipc_channel.cc中。

 

      从这里就可以看到,这个用来创建UNIX Socket的名字由当前进程的PID、一个顺序数和一个随机数通过"."符号连接而成的。

      回到RenderProcessHostImpl类的成员函数Init中,有了用来创建UNIX Socket的名字之后,就可以调用IPC::ChannelProxy类的静态成员函数Create创建一个Channel了,如下所示:

 

[cpp] view plaincopy

  1. scoped_ptr<ChannelProxy> ChannelProxy::Create(  
  2.     const IPC::ChannelHandle& channel_handle,  
  3.     Channel::Mode mode,  
  4.     Listener* listener,  
  5.     base::SingleThreadTaskRunner* ipc_task_runner) {  
  6.   scoped_ptr<ChannelProxy> channel(new ChannelProxy(listener, ipc_task_runner));  
  7.   channel->Init(channel_handle, mode, true);  
  8.   return channel.Pass();  
  9. }  

       这个函数定义在文件external/chromium_org/ipc/ipc_channel_proxy.cc中。

       IPC::ChannelProxy类的静态成员函数Create首先是创建了一个ChannelProxy对象,然后再调用该ChannelProxy对象的成员函数Init执行初始化工作,最后返回该ChannelProxy对象给调用者。

       ChannelProxy对象的创建过程如下所示:

 

[cpp] view plaincopy

  1. ChannelProxy::ChannelProxy(Listener* listener,  
  2.                            base::SingleThreadTaskRunner* ipc_task_runner)  
  3.     : context_(new Context(listener, ipc_task_runner)), did_init_(false) {  
  4. }  

      这个函数定义在文件external/chromium_org/ipc/ipc_channel_proxy.cc。

 

      ChannelProxy类的构造函数主要是创建一个ChannelProxy::Context对象,并且将该ChannelProxy::Context对象保存在成员变量context_中。

      ChannelProxy::Context对象的创建过程如下所示:

 

[cpp] view plaincopy

  1. ChannelProxy::Context::Context(Listener* listener,  
  2.                                base::SingleThreadTaskRunner* ipc_task_runner)  
  3.     : listener_task_runner_(base::ThreadTaskRunnerHandle::Get()),  
  4.       listener_(listener),  
  5.       ipc_task_runner_(ipc_task_runner),  
  6.       ......  
  7.       message_filter_router_(new MessageFilterRouter()),  
  8.       ...... {  
  9.   ......  
  10. }  

       这个函数定义在文件external/chromium_org/ipc/ipc_channel_proxy.cc中。

 

       ChannelProxy::Context类有三个成员变量是需要特别关注的,它们分别是:

       1. listenter_task_runner_。这个成员变量的类型为scoped_refptr<base::SingleThreadTaskRunner>,它指向的是一个SingleThreadTaskRunner对象。这个SingleThreadTaskRunner对象通过调用ThreadTaskRunnerHandle类的静态成员函数Get获得。从前面Chromium多线程模型设计和实现分析一文可以知道,ThreadTaskRunnerHandle类的静态成员函数Get返回的SingleThreadTaskRunner对象实际上是当前线程的一个MessageLoopProxy对象,通过该MessageLoopProxy对象可以向当前线程的消息队列发送消息。当前线程即为Browser进程的主线程。

       2. listener_。这是一个IPC::Listener指针,它的值设置为参数listener的值。从前面的图3可以知道,RenderProcessHostImpl类实现了IPC::Listener接口,而且从前面的调用过程过程可以知道,参数listener指向的就是一个RenderProcessHostImpl对象。以后正在创建的ChannelProxy::Context对象在IO线程中接收到Render进程发送过来的IPC消息之后,就会转发给成员变量listener_指向的RenderProcessHostImpl对象处理,但是并不是让后者直接在IO线程处理,而是让后者在成员变量listener_task_runner_描述的线程中处理,即Browser进程的主线程处理。也就是说,ChannelProxy::Context类的成员变量listener_task_runner_和listener_是配合在一起使用的,后面我们分析IPC消息的分发机制时就可以看到这一点。

       3. ipc_task_runner_。这个成员变量与前面分析的成员变量listener_task_runner一样,类型都为scoped_refptr<base::SingleThreadTaskRunner>,指向的者是一个SingleThreadTaskRunner对象。不过,这个SingleThreadTaskRunner对象由参数ipc_task_runner指定。从前面的调用过程可以知道,这个SingleThreadTaskRunner对象实际上是与Browser进程的IO线程关联的一个MessageLoopProxy对象。这个MessageLoopProxy对象用来接收Render进程发送过来的IPC消息。也就是说,Browser进程在IO线程中接收IPC消息。

       ChannelProxy::Context类还有一个重要的成员变量message_filter_router_,它指向一个MessageFilterRouter对象,用来过滤IPC消息,后面我们分析IPC消息的分发机制时再详细分析。

       回到ChannelProxy类的静态成员函数Create中,创建了一个ChannelProxy对象之后,接下来就调用它的成员函数Init进行初始化,如下所示:

 

[cpp] view plaincopy

  1. void ChannelProxy::Init(const IPC::ChannelHandle& channel_handle,  
  2.                         Channel::Mode mode,  
  3.                         bool create_pipe_now) {  
  4.   ......  
  5.   
  6.   if (create_pipe_now) {  
  7.     ......  
  8.     context_->CreateChannel(channel_handle, mode);  
  9.   } else {  
  10.     context_->ipc_task_runner()->PostTask(  
  11.         FROM_HERE, base::Bind(&Context::CreateChannel, context_.get(),  
  12.                               channel_handle, mode));  
  13.   }  
  14.   
  15.   // complete initialization on the background thread  
  16.   context_->ipc_task_runner()->PostTask(  
  17.       FROM_HERE, base::Bind(&Context::OnChannelOpened, context_.get()));  
  18.   
  19.   ......  
  20. }  

      这个函数定义在文件external/chromium_org/ipc/ipc_channel_proxy.cc中。      

 

      从前面的调用过程知道,参数channel_handle描述的是一个UNIX Socket名称,参数mode的值为IPC::Channel::MODE_SERVER,参数create_pipe_now的值为true。这样,ChannelProxy类的成员函数Init就会马上调用前面创建的ChannelProxy::Context对象的成员函数CreateChannel创建一个IPC通信通道,也就是在当前线程中创建一个IPC通信通道 。

      另一个方面,如果参数create_pipe_now的值等于false,那么ChannelProxy类的成员函数Init就不是在当前线程创建IPC通信通道,而是在IO线程中创建。因为它先通过前面创建的ChannelProxy::Context对象的成员函数ipc_task_runner获得其成员变量ipc_task_runner_描述的SingleThreadTaskRunner对象,然后再将创建IPC通信通道的任务发送到该SingleThreadTaskRunner对象描述的IO线程的消息队列去。当该任务被处理时,就会调用ChannelProxy::Context类的成员函数CreateChannel。

      当调用ChannelProxy::Context类的成员函数CreateChannel创建好一个IPC通信通道之后,ChannelProxy类的成员函数Init还会向当前进程的IO线程的消息队列发送一个消息,该消息绑定的是ChannelProxy::Context类的成员函数OnChannelOpened。因此,接下来我们就分别分析ChannelProxy::Context类的成员函数CreateChannel和OnChannelOpened。

       ChannelProxy::Context类的成员函数CreateChannel的实现如下所示:

 

[cpp] view plaincopy

  1. void ChannelProxy::Context::CreateChannel(const IPC::ChannelHandle& handle,  
  2.                                           const Channel::Mode& mode) {  
  3.   ......  
  4.   channel_ = Channel::Create(handle, mode, this);  
  5. }  

      这个函数定义在文件external/chromium_org/ipc/ipc_channel_proxy.cc中。   

 

      ChannelProxy::Context类的成员函数CreateChannel调用Channel类的成员函数Create创建了一个IPC通信通道,如下所示:

 

[cpp] view plaincopy

  1. scoped_ptr<Channel> Channel::Create(  
  2.     const IPC::ChannelHandle &channel_handle, Mode mode, Listener* listener) {  
  3.   return make_scoped_ptr(new ChannelPosix(  
  4.       channel_handle, mode, listener)).PassAs<Channel>();  
  5. }  

      这个函数定义在文件external/chromium_org/ipc/ipc_channel_posix.cc中。

 

      从这里可以看到,对于Android平台来说,IPC通信通道通过一个ChannelPosix对象描述,该ChannelPosix对象的创建过程如下所示:

 

[cpp] view plaincopy

  1. ChannelPosix::ChannelPosix(const IPC::ChannelHandle& channel_handle,  
  2.                            Mode mode, Listener* listener)  
  3.     : ChannelReader(listener),  
  4.       mode_(mode),  
  5.       ......  
  6.       pipe_(-1),  
  7.       client_pipe_(-1),  
  8. #if defined(IPC_USES_READWRITE)  
  9.       fd_pipe_(-1),  
  10.       remote_fd_pipe_(-1),  
  11. #endif  // IPC_USES_READWRITE  
  12.       pipe_name_(channel_handle.name),  
  13.       ...... {  
  14.   ......  
  15.   if (!CreatePipe(channel_handle)) {  
  16.     ......  
  17.   }  
  18. }  

       这个函数定义在文件external/chromium_org/ipc/ipc_channel_posix.cc中。

 

       从前面的调用过程可以知道,参数channel_handle描述的是一个UNIX Socket名称,参数mode的值等于IPC::Channel::MODE_SERVER,参数listener指向的是前面创建的ChannelProxy::Context对象。

       ChannelPosix类继承了ChannelReader类,后者用来读取从Render进程发送过来的IPC消息,并且将读取到的IPC消息发送给参数listener描述的ChannelProxy::Context对象,因此这里会将参数listener描述的ChannelProxy::Context对象传递给ChannelReader的构造函数。

       ChannelPosix类通过UNIX Socket来描述IPC通信通道,这个UNIX Socket的Server端和Client文件描述符分别保存在成员变量pipe_和client_pipe_中。如果定义了宏IPC_USES_READWRITE,那么当发送的消息包含有文件描述时,就会使用另外一个专用的UNIX Socket来传输文件描述符给对方。这个专用的UNIX Socket的Server端和Client端文件描述符保存在成员变量fd_pipe_和remote_fd_pipe_中。后面分析IPC消息的分发过程时,我们再详细分析这一点。

       ChannelPosix类的构造函数最后调用了另外一个成员函数CreatePipe开始创建IPC通信通道,如下所示:

 

[cpp] view plaincopy

  1. bool ChannelPosix::CreatePipe(  
  2.     const IPC::ChannelHandle& channel_handle) {  
  3.   ......  
  4.   
  5.   int local_pipe = -1;  
  6.   if (channel_handle.socket.fd != -1) {  
  7.     ......  
  8.   } else if (mode_ & MODE_NAMED_FLAG) {  
  9.     ......  
  10.   } else {  
  11.     local_pipe = PipeMap::GetInstance()->Lookup(pipe_name_);  
  12.     if (mode_ & MODE_CLIENT_FLAG) {  
  13.       if (local_pipe != -1) {  
  14.         ......  
  15.         local_pipe = HANDLE_EINTR(dup(local_pipe));  
  16.         ......  
  17.       } else {  
  18.         ......  
  19.   
  20.         local_pipe =  
  21.             base::GlobalDescriptors::GetInstance()->Get(kPrimaryIPCChannel);  
  22.       }  
  23.     } else if (mode_ & MODE_SERVER_FLAG) {  
  24.       ......  
  25.       base::AutoLock lock(client_pipe_lock_);  
  26.       if (!SocketPair(&local_pipe, &client_pipe_))  
  27.         return false;  
  28.       PipeMap::GetInstance()->Insert(pipe_name_, client_pipe_);  
  29.     }   
  30.     ......  
  31.   }  
  32.   
  33. #if defined(IPC_USES_READWRITE)  
  34.   // Create a dedicated socketpair() for exchanging file descriptors.  
  35.   // See comments for IPC_USES_READWRITE for details.  
  36.   if (mode_ & MODE_CLIENT_FLAG) {  
  37.     if (!SocketPair(&fd_pipe_, &remote_fd_pipe_)) {  
  38.       return false;  
  39.     }  
  40.   }  
  41. #endif  // IPC_USES_READWRITE  
  42.   
  43.   ......  
  44.   
  45.   pipe_ = local_pipe;  
  46.   return true;  
  47. }  

       这个函数定义在文件external/chromium_org/ipc/ipc_channel_posix.cc中。

 

       ChannelHandle类除了用来保存UNIX Socket的名称之外,还可以用来保存与该名称对应的UNIX Socket的文件描述符。在我们这个情景中,参数channel_handle仅仅保存了即将要创建的UNIX Socket的名称。

       ChannelPosix类的成员变量mode_的值等于IPC::Channel::MODE_SERVER,它的MODE_NAMED_FLAG位等于0。Render进程启动之后,也会调用到ChannelPosix类的成员函数CreatePipe创建一个Client端的IPC通信通道,那时候用来描述Client端IPC通信通道的ChannelPosix对象的成员变量mode_的值IPC::Channel::MODE_CLIENT,它的MODE_NAMED_FLAG位同样等于0。因此,无论是在Browser进程中创建的Server端IPC通信通道,还是在Render进程中创建的Client端IPC通信通道,在调用ChannelPosix类的成员函数CreatePipe时,都按照以下逻辑进行。

       对于Client端的IPC通信通道,即ChannelPosix类的成员变量mode_的MODE_CLIENT_FLAG位等于1的情况,首先是在一个Pipe Map中检查是否存在一个UNIX Socket文件描述符与成员变量pipe_name_对应。如果存在,那么就使用该文件描述符进行IPC通信。如果不存在,那么再到Global Descriptors中检查是否存在一个UNIX Socket文件描述符与常量kPrimaryIPCChannel对应。如果存在,那么就使用该文件描述符进行IPC通信。实际上,当网页不是在独立的Render进程中加载时,执行的是前一个逻辑,而当网页是在独立的Render进程中加载时,执行的是后一个逻辑。

       Chromium为了能够统一地处理网页在独立Render进程和不在独立Render进程加载两种情况,会对后者进行一个抽象,即会假设后者也是在独立的Render进程中加载一样。这样,Browser进程在加载该网页时,同样会创建一个图1所示的RenderProcess对象,不过该RenderProcess对象没有对应的一个真正的进程,对应的仅仅是Browser进程中的一个线程。也就是这时候,图1所示的RenderPocessHost对象和RenderProcess对象执行的仅仅是进程内通信而已,不过它们仍然是按照进程间的通信规则进行,也就是通过IO线程来间接进行。不过,在进程内建立IPC通信通道和在进程间建立IPC通信通道的方式是不一样的。具体来说,就是在进程间建立IPC通信通道,需要将描述该通道的UNIX Socket的Client端文件描述符从Browser进程传递到Render进程,Render进程接收到该文件描述符之后,就会以kPrimaryIPCChannel为键值保存在Global Descriptors中。而在进程内建立IPC通信通道时,描述IPC通信通道的UNIX Socket的Client端文件描述符直接以UNIX Socket名称为键值,保存在一个Pipe Map中即可。后面我们分析在进程内在进程间创建Client端IPC通信通道时,会继续看到这些相关的区别。

      对于Server端的IPC通信通道,即ChannelPosix类的成员变量mode_的MODE_SERVER_FLAG位等于1的情况,ChannelPosix类的成员函数CreatePipe调用函数SocketPair创建了一个UNIX Socket,其中,Server端文件描述符保存在成员变量pipe_中,而Client端文件描述符保存在成员变量client_pipe_中,并且Client端文件描述符还会以与前面创建的UNIX Socket对应的名称为键值,保存在一个Pipe Map中,这就是为建立进程内IPC通信通道而准备的。

       最后,如果定义了IPC_USES_READWRITE宏,如前面提到的,那么还会继续创建一个专门用来在进程间传递文件描述的UNIX Socket,该UNIX Socket的Server端和Client端文件描述符分别保存在成员变量fd_pipe_和remote_fd_pipe_中。

       这一步执行完成之后,一个Server端IPC通信通道就创建完成了。回到ChannelProxy类的成员函数Init中,它接下来是发送一个消息到Browser进程的IO线程的消息队列中,该消息绑定的是ChannelProxy::Context类的成员函数OnChannelOpened,它的实现如下所示:

 

[cpp] view plaincopy

  1. void ChannelProxy::Context::OnChannelOpened() {  
  2.   ......  
  3.   
  4.   if (!channel_->Connect()) {  
  5.     OnChannelError();  
  6.     return;  
  7.   }  
  8.   
  9.   ......  
  10. }  

      这个函数定义在文件external/chromium_org/ipc/ipc_channel_proxy.cc中。

 

      从前面的分析可以知道,ChannelProxy::Context类的成员变量channel_指向的是一个ChannelPosix对象,这里调用它的成员函数Connect将它描述的IPC通信通道交给当前进程的IO线程进行监控。

      ChannelPosix类的成员函数Connect的实现如下所示:

 

[cpp] view plaincopy

  1. bool ChannelPosix::Connect() {  
  2.   ......  
  3.   
  4.   bool did_connect = true;  
  5.   if (server_listen_pipe_ != -1) {  
  6.     ......  
  7.   } else {  
  8.     did_connect = AcceptConnection();  
  9.   }  
  10.   return did_connect;  
  11. }  

      这个函数定义在文件external/chromium_org/ipc/ipc_channel_proxy.cc中。

      当ChannelPosix类的成员变量server_listen_pipe_的值不等于-1时,表示它描述的是一个用来负责监听IPC通信通道连接消息的Socket中,也就是这个Socket不是真正用来执行Browser进程和Render进程之间的通信的,而是Browser进程首先对ChannelPosix类的成员变量server_listen_pipe_描述的Socket进行listen,接着Render进程通过connect连接到该Socket,使得Browser进程accepet到一个新的Socket,然后再通过这个新的Socket与Render进程执行IPC。

      在我们这个情景中,ChannelPosix类的成员变量server_listen_pipe_的值等于-1,因此接下来ChannelPosix类的成员函数Connect调用了另外一个成员函数AcceptConnection,它的实现如下所示:

 

[cpp] view plaincopy

  1. bool ChannelPosix::AcceptConnection() {  
  2.   base::MessageLoopForIO::current()->WatchFileDescriptor(  
  3.       pipe_, true, base::MessageLoopForIO::WATCH_READ, &read_watcher_, this);  
  4.   QueueHelloMessage();  
  5.   
  6.   if (mode_ & MODE_CLIENT_FLAG) {  
  7.     // If we are a client we want to send a hello message out immediately.  
  8.     // In server mode we will send a hello message when we receive one from a  
  9.     // client.  
  10.     waiting_connect_ = false;  
  11.     return ProcessOutgoingMessages();  
  12.   } else if (mode_ & MODE_SERVER_FLAG) {  
  13.     waiting_connect_ = true;  
  14.     return true;  
  15.   } else {  
  16.     NOTREACHED();  
  17.     return false;  
  18.   }  
  19. }  

       这个函数定义在文件external/chromium_org/ipc/ipc_channel_proxy.cc中。

 

       ChannelPosix类的成员函数AcceptConnection首先是获得与当前进程的IO线程关联的一个MessageLoopForIO对象,接着再调用该MessageLoopForIO对象的成员函数WatchFileDescriptor对成员变量pipe_ 描述的一个UNIX Socket进行监控。MessageLoopForIO类的成员函数WatchFileDescriptor最终会调用到在前面Chromium多线程模型设计和实现分析一文中提到的MessagePumpLibevent对该UNIX Socket进行监控。这意味着当该UNIX Socket有新的IPC消息需要接收时,当前正在处理的ChannelPosix对象的成员函数OnFileCanReadWithoutBlocking就会被调用。这一点需要理解Chromium的多线程机制,具体可以参考Chromium多线程模型设计和实现分析一文。

       接下来,ChannelPosix类的成员函数AcceptConnection还会调用另外一个成员函数QueueHelloMessage创建一个Hello Message,并且将该Message添加到内部的一个IPC消息队列去等待发送给对方进程。执行IPC的双方,就是通过这个Hello Message进行握手的。具体来说,就是Server端和Client端进程建立好连接之后,由Client端发送一个Hello Message给Server端,Server端接收到该Hello Message之后,就认为双方已经准备就绪,可以进行IPC了。

       因此,如果当前正在处理的ChannelPosix对象描述的是Client端的通信通道,即它的成员变量mode_的MODE_CLIENT_FLAG位等于1,那么ChannelPosix类的成员函数AcceptConnection就会马上调用另外一个成员函数ProcessOutgoingMessages前面创建的Hello Message发送给Server端。

       另一方面,如果当前正在处理的ChannelPosix对象描述的是Server端的通信通道,那么ChannelPosix类的成员函数AcceptConnection就仅仅是将成员变量waiting_connect_的值设置为true,表示正在等待Client端发送一个Hello Message过来。

       关于Hello Message的发送和接收,我们在接下来的一篇文章分析IPC消息分发机制时再详细分析。

       这一步执行完成之后,Server端的IPC通信通道就创建完成了,也就是Browser进程已经创建好了一个Server端的IPC通信通道。回到RenderProcessHostImpl类的成员函数Init中,它接下来要做的事情就是启动Render进程。

       我们首先考虑网页不是在独立的Render进程加载的情况,即在Browser进程加载的情况,这时候并没有真的启动了一个Render进程,而仅仅是在Browser进程中创建了一个RenderProcess对象而已,如下所示:

 

[cpp] view plaincopy

  1. bool RenderProcessHostImpl::Init() {  
  2.   ......  
  3.   
  4.   // Setup the IPC channel.  
  5.   const std::string channel_id =  
  6.       IPC::Channel::GenerateVerifiedChannelID(std::string());  
  7.   channel_ = IPC::ChannelProxy::Create(  
  8.       channel_id,  
  9.       IPC::Channel::MODE_SERVER,  
  10.       this,  
  11.       BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO).get());  
  12.   
  13.   ......  
  14.   
  15.   if (run_renderer_in_process()) {  
  16.     ......  
  17.     in_process_renderer_.reset(g_renderer_main_thread_factory(channel_id));  
  18.   
  19.     base::Thread::Options options;  
  20.     ......  
  21.     options.message_loop_type = base::MessageLoop::TYPE_DEFAULT;  
  22.       
  23.     in_process_renderer_->StartWithOptions(options);  
  24.   
  25.     g_in_process_thread = in_process_renderer_->message_loop();  
  26.   
  27.     ......  
  28.   } else {  
  29.     ......  
  30.   }  
  31.   
  32.   return true;  
  33. }  

       这个函数定义在文件external/chromium_org/content/browser/renderer_host/render_process_host_impl.cc中。

 

       前面在分析RenderProcessHostImpl类的成员函数Init时提到,RenderProcessHostImpl类的静态成员变量g_renderer_main_thread_factory描述的是一个函数,通过它可以创建一个类型为InProcessRendererThread的线程。

       一个类型为InProcessRendererThread的线程的创建过程如下所示:

 

[cpp] view plaincopy

  1. InProcessRendererThread::InProcessRendererThread(const std::string& channel_id)  
  2.     : Thread("Chrome_InProcRendererThread"), channel_id_(channel_id) {  
  3. }  

      这个函数定义在文件external/chromium_org/content/renderer/in_process_renderer_thread.cc中。

 

      从这里就可以看到,InProcessRendererThread类是从Thread类继承下来的,因此这里调用了Thread类的构造函数。

      此外,InProcessRendererThread类的构造函数还会将参数channel_id描述的一个UNIX Socket名称保存在成员变量channel_id_中。从前面的分析可以知道,该名称对应的UNIX Socket已经创建出来了,并且它的Client端文件描述符以该名称为键值,保存在一个Pipe Map中。

      回到RenderProcessHostImpl类的成员函数Init中,接下来它会调用前面创建的InProcessRendererThread对象的成员函数StartWithOptions启动一个线程。从前面Chromium多线程模型设计和实现分析一文可以知道,当该线程启动起来之后,并且在进入消息循环之前,会被调用InProcessRendererThread类的成员函数Init执行初始化工作。

      InProcessRendererThread类的成员函数Init的实现如下所示:

 

[cpp] view plaincopy

  1. void InProcessRendererThread::Init() {  
  2.   render_process_.reset(new RenderProcessImpl());  
  3.   new RenderThreadImpl(channel_id_);  
  4. }  

      这个函数定义在文件external/chromium_org/content/renderer/in_process_renderer_thread.cc中。

 

      InProcessRendererThread类的成员函数Init首先在当前进程,即Browser进程,创建了一个RenderProcessImpl对象,保存在成员变量render_process_中,描述一个假的Render进程,接着再创建了一个RenderThreadImpl对象描述当前线程,即当前正在处理的InProcessRendererThread对象描述的线程。

     在RenderProcessImpl对象的创建中,会创建一个IO线程,该IO线程负责与Browser进程启动时就创建的一个IO线程执行IPC通信。从图6可以知道,RenderProcessImpl类继承了RenderProcess类,RenderProcess类又继承了ChildProcess类,创建IO线程的工作是从ChildProcess类的构造函数中进行的,如下所示:

 

[cpp] view plaincopy

  1. ChildProcess::ChildProcess()  
  2.     : ...... {  
  3.   ......  
  4.   
  5.   // We can't recover from failing to start the IO thread.  
  6.   CHECK(io_thread_.StartWithOptions(  
  7.       base::Thread::Options(base::MessageLoop::TYPE_IO, 0)));  
  8.   
  9.   ......  
  10. }  

      这个函数定义在文件external/chromium_org/content/child/child_process.cc中。

 

      从这里就可以看到,ChildProcess类的构造函数调用了成员变量io_thread_描述的一个Thread对象的成员函数StartWithOptions创建了一个IO线程。

      回到InProcessRendererThread类的成员函数Init中,在RenderThreadImpl对象的创建过程,会创建一个Client端的IPC通信通道,如下所示:

 

[cpp] view plaincopy

  1. RenderThreadImpl::RenderThreadImpl(const std::string& channel_name)  
  2.     : ChildThread(channel_name) {  
  3.   ......  
  4. }  

      这个函数定义在文件external/chromium_org/content/renderer/render_thread_impl.cc中。

      从这里可以看到,RenderThreadImpl类继承了ChildThread类,创建Client端IPC通信通道的过程是在ChildThread类的构造函数中进行的,如下所示:

 

[cpp] view plaincopy

  1. ChildThread::ChildThread(const std::string& channel_name)  
  2.     : channel_name_(channel_name),  
  3.       ..... {  
  4.   Init();  
  5. }  

       这个函数定义在文件external/chromium_org/content/child/child_thread.cc中。

 

       ChildThread类的构造函数将参数channel_name描述的一个UNIX Socket的名称保存在成员变量channel_name_之后,就调用了另外一个成员函数Init执行创建Client端IPC通信通道的工作,如下所示: 

 

[cpp] view plaincopy

  1. void ChildThread::Init() {  
  2.   ......  
  3.   
  4.   channel_ =  
  5.       IPC::SyncChannel::Create(channel_name_,  
  6.                                IPC::Channel::MODE_CLIENT,  
  7.                                this,  
  8.                                ChildProcess::current()->io_message_loop_proxy(),  
  9.                                true,  
  10.                                ChildProcess::current()->GetShutDownEvent());  
  11.   
  12.   ......  
  13. }  

      这个函数定义在文件external/chromium_org/content/child/child_thread.cc中。

 

      Client端IPC通信通道通过IPC::SyncChannel类的静态成员函数Create进行创建,如下所示:

 

[cpp] view plaincopy

  1. scoped_ptr<SyncChannel> SyncChannel::Create(  
  2.     const IPC::ChannelHandle& channel_handle,  
  3.     Channel::Mode mode,  
  4.     Listener* listener,  
  5.     base::SingleThreadTaskRunner* ipc_task_runner,  
  6.     bool create_pipe_now,  
  7.     base::WaitableEvent* shutdown_event) {  
  8.   scoped_ptr<SyncChannel> channel =  
  9.       Create(listener, ipc_task_runner, shutdown_event);  
  10.   channel->Init(channel_handle, mode, create_pipe_now);  
  11.   return channel.Pass();  
  12. }  

       这个函数定义在文件external/chromium_org/ipc/ipc_sync_channel.cc中。

 

       IPC::SyncChannel类的静态成员函数Create首先调用另外一个重载版本的静态成员函数Create创建一个SyncChannel对象,接着再调用该SyncChannel的成员函数Init执行初始化工作。

       IPC::SyncChannel类是从IPC::ChannelProxy类继承下来的,它与IPC::ChannelProxy的区别在于,前者既可以用来发送同步的IPC消息,也可以用来发送异步的IPC消息,而后者只可以用来发送异步消息。所谓同步IPC消息,就是发送者发送它给对端之后,会一直等待对方发送一个回复,而对于异步IPC消息,发送者把它发送给对端之后,不会进行等待,而是直接返回。后面分析IPC消息的分发机制时我们再详细分析这一点。

       IPC::SyncChannel类的成员函数Init是从父类IPC::ChannelProxy类继承下来的,后者我们前面已经分析过了,主要区别在于这里传递第二个参数mode的值等于IPC::Channel::MODE_CLIENT,表示要创建的是一个Client端的IPC通信通道。

       接下来,我们就主要分析IPC::SyncChannel类三个参数版本的静态成员函数Create创建SyncChannel对象的过程,如下所示:

 

[cpp] view plaincopy

  1. scoped_ptr<SyncChannel> SyncChannel::Create(  
  2.     Listener* listener,  
  3.     base::SingleThreadTaskRunner* ipc_task_runner,  
  4.     WaitableEvent* shutdown_event) {  
  5.   return make_scoped_ptr(  
  6.       new SyncChannel(listener, ipc_task_runner, shutdown_event));  
  7. }  

      这个函数定义在文件external/chromium_org/ipc/ipc_sync_channel.cc中。

 

      IPC::SyncChannel类三个参数版本的静态成员函数Create创建了一个SyncChannel对象,并且将该SyncChannel对象返回给调用者。

      SyncChannel对象的创建过程如下所示:

 

[cpp] view plaincopy

  1. SyncChannel::SyncChannel(  
  2.     Listener* listener,  
  3.     base::SingleThreadTaskRunner* ipc_task_runner,  
  4.     WaitableEvent* shutdown_event)  
  5.     : ChannelProxy(new SyncContext(listener, ipc_task_runner, shutdown_event)) {  
  6.   ......  
  7.   StartWatching();  
  8. }  

      这个函数定义在文件external/chromium_org/ipc/ipc_sync_channel.cc中。

 

      从前面的调用过程可以知道,参数listener描述的是一个ChildThread对象,参数ipc_task_runner描述的是与前面在ChildProcess类的构造函数中创建的IO线程关联的一个MessageLoopProxy对象,参数shutdown_event描述的是一个ChildProcess关闭事件。

      对于第三个参数shutdown_event的作用,我们这里做一个简单的介绍,在接下来一篇文章中分析IPC消息的分发机制时再详细分析。前面提到,SyncChannel可以用来发送同步消息,这意味着发送线程需要进行等待。这个等待过程是通过我们在前面Chromium多线程模型设计和实现分析一文中提到的WaitableEvent类实现的。也就是说,每一个同步消息都有一个关联的WaitableEvent对象。此外,还有一些异常情况需要处理。例如,SyncChannel在等待一个同步消息的过程中,有可能对方已经退出了,这相当于是发生了一个ChildProcess关闭事件。在这种情况下,继续等待是没有意义的。因此,当SyncChannel监控到ChildProcess关闭事件时,就可以执行一些清理工作了。此外,SyncChannel在等待一个同步消息的过程中,也有可能收到对方发送过来的非回复消息。在这种情况下,SyncChannel需要获得通知,以便可以对这些非回复消息进行处理。SyncChannel获得此类非回复消息的事件通知是通过另外一个称为Dispatch Event的WaitableEvent对象获得的。这意味着SyncChannel在发送一个同步消息的过程中,需要同时监控多个WaitableEvent对象。

      了解了各个参数的含义之后,我们就开始分析SyncChannel类的构造函数。它首先是创建了一个SyncChannel::SyncContext对象,并且以该SyncChannel::SyncContext对象为参数,调用父类ChannelProxy的构造函数,以便可以对父类ChannelProxy进行初始化。

       SyncChannel::SyncContext对象的创建过程如下所示:

 

[cpp] view plaincopy

  1. SyncChannel::SyncContext::SyncContext(  
  2.     Listener* listener,  
  3.     base::SingleThreadTaskRunner* ipc_task_runner,  
  4.     WaitableEvent* shutdown_event)  
  5.     : ChannelProxy::Context(listener, ipc_task_runner),  
  6.       ......,  
  7.       shutdown_event_(shutdown_event),  
  8.       ...... {  
  9. }  

       这个函数定义在文件external/chromium_org/ipc/ipc_sync_channel.cc中。

 

       从这里可以看到,SyncChannel::SyncContext类是从ChannelProxy::Context类继承下来的,因此这里会调用ChannelProxy::Context类的构造函数进行初始化。此外,SyncChannel::SyncContext类的构造函数还会将参数shutdown_event描述的一个ChildProcess关闭事件保存在成员变量shutdown_event_中。

       回到SyncChannel类的构造函数中,当它创建了一个SyncChannel::SyncContext对象之后,就使用该SyncChannel::SyncContext对象来初始化父类ChannelProxy,如下所示:

 

[cpp] view plaincopy

  1. ChannelProxy::ChannelProxy(Context* context)  
  2.     : context_(context),   
  3.       did_init_(false) {  
  4. }  

      这个函数定义在文件external/chromium_org/ipc/ipc_channel_proxy.cc。

 

      注意,参数context的类型虽然为一个ChannelProxy::Context指针,但是它实际上指向的是一个SyncChannel::SyncContext对象,该SyncChannel::SyncContext对象保存在成员变量context_中。

      继续回到SyncChannel类的构造函数中,它用一个SyncChannel::SyncContext对象初始化了父类ChannelProxy之后,继续调用另外一个成员函数StartWatching监控我们在前面提到的一个Dispatch Event,如下所示:

 

[cpp] view plaincopy

  1. void SyncChannel::StartWatching() {  
  2.   ......  
  3.   dispatch_watcher_callback_ =  
  4.       base::Bind(&SyncChannel::OnWaitableEventSignaled,  
  5.                   base::Unretained(this));  
  6.   dispatch_watcher_.StartWatching(sync_context()->GetDispatchEvent(),  
  7.                                   dispatch_watcher_callback_);  
  8. }  

      这个函数定义在文件external/chromium_org/ipc/ipc_sync_channel.cc中。

 

      SyncChannel类的成员函数StartWatching调用成员变量dispatch_watcher_描述的一个WaitableEventWatcher对象的成员函数StartWatching对Dispatch Event进行监控,从这里就可以看到,Dispatch Event可以通过前面创建的SyncChannel::SyncContext对象的成员函数sync_context获得,并且当该Display Event发生时,SyncChannel类的成员函数OnWaitableEventSignaled就会被调用。

      前面在分析ChannelProxy类的成员函数Init时,我们提到,当它调用另外一个成员函数CreateChannel创建了一个IPC通信通道之后,会调用其成员变量context_描述的一个ChannelProxy::Context对象的成员函数OnChannelOpened将已经创建好的的IPC通信通道增加到IO线程的消息队列中去监控。由于在我们这个情景中,ChannelProxy类的成员变量context_指向的是一个SyncChannel::SyncContext对象,因此,当ChannelProxy类的成员函数Init创建了一个IPC通信通道之后,它接下来调用的是SyncChannel::SyncContext类的成员函数OnChanneIOpened将已经创建好的IPC通信通道增加到IO线程的消息队列中去监控。

      SyncChannel::SyncContext类的成员函数OnChanneIOpened的实现如下所示:

 

[cpp] view plaincopy

  1. void SyncChannel::SyncContext::OnChannelOpened() {  
  2.   shutdown_watcher_.StartWatching(  
  3.       shutdown_event_,  
  4.       base::Bind(&SyncChannel::SyncContext::OnWaitableEventSignaled,  
  5.                  base::Unretained(this)));  
  6.   Context::OnChannelOpened();  
  7. }  

      这个函数定义在文件external/chromium_org/ipc/ipc_sync_channel.cc中。

 

      SyncChannel::SyncContext类的成员函数OnChanneIOpened首先是调用成员变量shutdown_watcher_描述的一个WaitableEventWatcher对象的成员函数StartWatching监控成员变量shutdown_event_描述的一个ChildProcess关闭事件。从这里就可以看到,当ChildProcess关闭事件发生时,SyncChannel::SyncContext类的成员函数OnWaitableEventSignaled就会被调用。

      最后,SyncChannel::SyncContext类的成员函数OnChanneIOpened调用了父类ChannelProxy的成员函数OnChannelOpened将IPC通信通道增加到IO线程的的消息队列中去监控。

      这一步执行完成之后,一个Client端的IPC通信通道就创建完成了。这里我们描述的Client端IPC通信通道的创建过程虽然是发生在Browser进程中的,不过这个过程与在独立的Render进程中创建的Client端IPC通信通道的过程是一样的。这一点在接下来的分析中就可以看到。

      回到前面分析的RenderProcessHostImpl类的成员函数Init中,对于需要在独立的Render进程加载网页的情况,它就会启动一个Render进程,如下所示:

 

[cpp] view plaincopy

  1. bool RenderProcessHostImpl::Init() {  
  2.   ......  
  3.   
  4.   // Setup the IPC channel.  
  5.   const std::string channel_id =  
  6.       IPC::Channel::GenerateVerifiedChannelID(std::string());  
  7.   channel_ = IPC::ChannelProxy::Create(  
  8.       channel_id,  
  9.       IPC::Channel::MODE_SERVER,  
  10.       this,  
  11.       BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO).get());  
  12.   
  13.   ......  
  14.   
  15.   if (run_renderer_in_process()) {  
  16.     ......  
  17.   } else {  
  18.     ......  
  19.   
  20.     CommandLine* cmd_line = new CommandLine(renderer_path);  
  21.     ......  
  22.     AppendRendererCommandLine(cmd_line);  
  23.     cmd_line->AppendSwitchASCII(switches::kProcessChannelID, channel_id);  
  24.   
  25.     ......  
  26.   
  27.     child_process_launcher_.reset(new ChildProcessLauncher(  
  28.         new RendererSandboxedProcessLauncherDelegate(channel_.get()),  
  29.         cmd_line,  
  30.         GetID(),  
  31.         this));  
  32.   
  33.     ......  
  34.   }  
  35.   
  36.   return true;  
  37. }  

       这个函数定义在文件external/chromium_org/content/browser/renderer_host/render_process_host_impl.cc中。

 

       RenderProcessHostImpl类的成员函数Init创建了一个Server端的IPC通信通道之后,就会通过一个ChildProcessLauncher对象来启动一个Render进程。不过在启动该Render进程之前,首先要构造好它的启动参数,也就是命令行参数。

       Render进程的启动命令行参数通过一个CommandLine对象来描述,它包含有很多选项,不过现在我们只关心两个。一个是switches::kProcessType,另外一个是switches::kProcessChannelID。其中,switches::kProcessChannelID选项对应的值设置为本地变量channel_id描述的值,即前面调用IPC::Channel类的静态成员函数GenerateVerifiedChannelID生成的一个UNIX Socket名称。

       选项switches::kProcessType的值是通过RenderProcessHostImpl类的成员函数AppendRendererCommandLine设置的,如下所示:

 

[cpp] view plaincopy

  1. void RenderProcessHostImpl::AppendRendererCommandLine(  
  2.     CommandLine* command_line) const {  
  3.   // Pass the process type first, so it shows first in process listings.  
  4.   command_line->AppendSwitchASCII(switches::kProcessType,  
  5.                                   switches::kRendererProcess);  
  6.     
  7.     ......  
  8. }  

       这个函数定义在文件external/chromium_org/content/browser/renderer_host/render_process_host_impl.cc中。

 

       从这里就可以看到,选项switches::kProcessType的值设置为kRendererProcess,这表示接下来我们通过ChildProcessLauncher类启动的进程是一个Render进程。

       回到RenderProcessHostImpl类的成员函数Init中,当要启动的Render进程的命令行参数准备好之后,接下来就通过ChildProcessLauncher类的构造函数启动一个Render进程,如下所示:

 

[cpp] view plaincopy

  1. ChildProcessLauncher::ChildProcessLauncher(  
  2.     SandboxedProcessLauncherDelegate* delegate,  
  3.     CommandLine* cmd_line,  
  4.     int child_process_id,  
  5.     Client* client) {  
  6.   context_ = new Context();  
  7.   context_->Launch(  
  8.       delegate,  
  9.       cmd_line,  
  10.       child_process_id,  
  11.       client);  
  12. }  

       这个函数定义在文件external/chromium_org/content/browser/child_process_launcher.cc中。

       ChildProcessLauncher类的构造函数首先创建了一个ChildProcessLauncher::Context对象,保存在成员变量context_中,并且调用该ChildProcessLauncher::Context对象的成员函数Launch启动一个Render进程。

       ChildProcessLauncher::Context类的成员函数Launch的实现如下所示:

 

[cpp] view plaincopy

  1. class ChildProcessLauncher::Context  
  2.     : public base::RefCountedThreadSafe<ChildProcessLauncher::Context> {  
  3.  public:  
  4.   ......  
  5.   
  6.   void Launch(  
  7.       SandboxedProcessLauncherDelegate* delegate,  
  8.       CommandLine* cmd_line,  
  9.       int child_process_id,  
  10.       Client* client) {  
  11.     client_ = client;  
  12.   
  13.     ......  
  14.   
  15.     BrowserThread::PostTask(  
  16.         BrowserThread::PROCESS_LAUNCHER, FROM_HERE,  
  17.         base::Bind(  
  18.             &Context::LaunchInternal,  
  19.             make_scoped_refptr(this),  
  20.             client_thread_id_,  
  21.             child_process_id,  
  22.             delegate,  
  23.             cmd_line));  
  24.   }  
  25.   
  26.   ......  
  27. };  

       这个函数定义在文件external/chromium_org/content/browser/child_process_launcher.cc中。

 

       ChildProcessLauncher::Context类的成员函数Launch通过调用BrowserThread类的静态成员函数PostTask向Browser进程的一个专门用来启动子进程的BrowserThread::PROCESS_LAUNCHER线程的消息队列发送一个任务,该任务绑定了ChildProcessLauncher::Context类的成员函数LaunchInternal。因此,接下来ChildProcessLauncher::Context类的成员函数LaunchInternal就会在BrowserThread::PROCESS_LAUNCHER线程中执行,如下所示:

 

[cpp] view plaincopy

  1. class ChildProcessLauncher::Context  
  2.     : public base::RefCountedThreadSafe<ChildProcessLauncher::Context> {  
  3.  public:  
  4.   ......  
  5.   
  6.   static void LaunchInternal(  
  7.       // |this_object| is NOT thread safe. Only use it to post a task back.  
  8.       scoped_refptr<Context> this_object,  
  9.       BrowserThread::ID client_thread_id,  
  10.       int child_process_id,  
  11.       SandboxedProcessLauncherDelegate* delegate,  
  12.       CommandLine* cmd_line) {  
  13.     ......  
  14.     int ipcfd = delegate->GetIpcFd();  
  15.     ......  
  16.   
  17.     std::vector<FileDescriptorInfo> files_to_register;  
  18.     files_to_register.push_back(  
  19.         FileDescriptorInfo(kPrimaryIPCChannel,  
  20.                            base::FileDescriptor(ipcfd, false)));  
  21.     ......  
  22.   
  23.     StartChildProcess(cmd_line->argv(), child_process_id, files_to_register,  
  24.         base::Bind(&ChildProcessLauncher::Context::OnChildProcessStarted,  
  25.                    this_object, client_thread_id, begin_launch_time));  
  26.   
  27.     ......  
  28.   }  
  29.   
  30.   ......  
  31. };  

       这个函数定义在文件external/chromium_org/content/browser/child_process_launcher.cc中。

 

       从前面的调用过程可以知道,参数delegate指向的一个SandboxedProcessLauncherDelegate对象封装了前面创建的一个ChannelProxy对象,通过调用它的成员函数GetIpcFd可以获得它所封装的ChannelProxy对象描述的一个UNIX Socket的Client端文件描述符。该Client端文件描述符接下来以kPrimaryIPCChannel为键值封装在一个FileDescriptorInfo对象,并且该FileDescriptorInfo对象最终会保存在本地变量files_to_register描述的一个std::vector。该std::vector最后又会传递给函数StartChildProcess,后者负责执行启动Render进程的工程。

       函数StartChildProcess的实现如下所示:

 

[cpp] view plaincopy

  1. void StartChildProcess(  
  2.     const CommandLine::StringVector& argv,  
  3.     int child_process_id,  
  4.     const std::vector<content::FileDescriptorInfo>& files_to_register,  
  5.     const StartChildProcessCallback& callback) {  
  6.   JNIEnv* env = AttachCurrentThread();  
  7.   ......  
  8.   
  9.   // Create the Command line String[]  
  10.   ScopedJavaLocalRef<jobjectArray> j_argv = ToJavaArrayOfStrings(env, argv);  
  11.   
  12.   size_t file_count = files_to_register.size();  
  13.   DCHECK(file_count > 0);  
  14.   
  15.   ScopedJavaLocalRef<jintArray> j_file_ids(env, env->NewIntArray(file_count));  
  16.   base::android::CheckException(env);  
  17.   jint* file_ids = env->GetIntArrayElements(j_file_ids.obj(), NULL);  
  18.   base::android::CheckException(env);  
  19.   ScopedJavaLocalRef<jintArray> j_file_fds(env, env->NewIntArray(file_count));  
  20.   base::android::CheckException(env);  
  21.   jint* file_fds = env->GetIntArrayElements(j_file_fds.obj(), NULL);  
  22.   base::android::CheckException(env);  
  23.   ScopedJavaLocalRef<jbooleanArray> j_file_auto_close(  
  24.       env, env->NewBooleanArray(file_count));  
  25.   base::android::CheckException(env);  
  26.   jboolean* file_auto_close =  
  27.       env->GetBooleanArrayElements(j_file_auto_close.obj(), NULL);  
  28.   base::android::CheckException(env);  
  29.   for (size_t i = 0; i < file_count; ++i) {  
  30.     const content::FileDescriptorInfo& fd_info = files_to_register[i];  
  31.     file_ids[i] = fd_info.id;  
  32.     file_fds[i] = fd_info.fd.fd;  
  33.     file_auto_close[i] = fd_info.fd.auto_close;  
  34.   }  
  35.   env->ReleaseIntArrayElements(j_file_ids.obj(), file_ids, 0);  
  36.   env->ReleaseIntArrayElements(j_file_fds.obj(), file_fds, 0);  
  37.   env->ReleaseBooleanArrayElements(j_file_auto_close.obj(), file_auto_close, 0);  
  38.   
  39.   Java_ChildProcessLauncher_start(env,  
  40.       base::android::GetApplicationContext(),  
  41.       j_argv.obj(),  
  42.       child_process_id,  
  43.       j_file_ids.obj(),  
  44.       j_file_fds.obj(),  
  45.       j_file_auto_close.obj(),  
  46.       reinterpret_cast<intptr_t>(new StartChildProcessCallback(callback)));  
  47. }  

       这个函数定义在文件external/chromium_org/content/browser/android/child_process_launcher_android.cc中。

 

       函数StartChildProcess将参数argv转换为一个Java层的String数组,并且将参数files_to_register描述的一个类型为FileDescriptorInfo的std::vector转换为两个Java层的int数组以及一个boolean数组,其中,第一个int数组描述的一组ID,第二个int数组描述的是与第一个int数组描述的ID对应的一组文件描述符,而另外一个boolean数组描述第二个int数组描述的一组文件描述符那些是需要自动关闭的。

       最后,函数StartChildProcess最后调用了另外一个函数Java_ChildProcessLauncher_start通过Java层的接口来启动一个子进程,如下所示:

 

[cpp] view plaincopy

  1. static void Java_ChildProcessLauncher_start(JNIEnv* env, jobject context,  
  2.     jobjectArray commandLine,  
  3.     JniIntWrapper childProcessId,  
  4.     jintArray fileIds,  
  5.     jintArray fileFds,  
  6.     jbooleanArray fileAutoClose,  
  7.     jlong clientContext) {  
  8.   /* Must call RegisterNativesImpl()  */  
  9.   CHECK_CLAZZ(env, ChildProcessLauncher_clazz(env),  
  10.       ChildProcessLauncher_clazz(env));  
  11.   jmethodID method_id =  
  12.       base::android::MethodID::LazyGet<  
  13.       base::android::MethodID::TYPE_STATIC>(  
  14.       env, ChildProcessLauncher_clazz(env),  
  15.       "start",  
  16.   
  17. "("  
  18. "Landroid/content/Context;"  
  19. "[Ljava/lang/String;"  
  20. "I"  
  21. "[I"  
  22. "[I"  
  23. "[Z"  
  24. "J"  
  25. ")"  
  26. "V",  
  27.       &g_ChildProcessLauncher_start);  
  28.   
  29.      env->CallStaticVoidMethod(ChildProcessLauncher_clazz(env),  
  30.           method_id, context, commandLine, as_jint(childProcessId), fileIds,  
  31.               fileFds, fileAutoClose, clientContext);  
  32.   jni_generator::CheckException(env);  
  33.   
  34. }  

      这个函数定义在文件out/target/product/generic/obj/GYP/shared_intermediates/content/jni/ChildProcessLauncher_jni.h中。

 

      函数Java_ChildProcessLauncher_start调用参数env描述的一个JNI环境对象调用了Java层的ChildProcessLauncher类的成员函数start执行启动子进程的操作。

      Java层的ChildProcessLauncher类的成员函数start的实现如下所示:

 

[java] view plaincopy

  1. public class ChildProcessLauncher {  
  2.     ......  
  3.   
  4.     static void start(  
  5.             Context context,  
  6.             final String[] commandLine,  
  7.             int childProcessId,  
  8.             int[] fileIds,  
  9.             int[] fileFds,  
  10.             boolean[] fileAutoClose,  
  11.             long clientContext) {  
  12.         ......  
  13.         FileDescriptorInfo[] filesToBeMapped = new FileDescriptorInfo[fileFds.length];  
  14.         for (int i = 0; i < fileFds.length; i++) {  
  15.             filesToBeMapped[i] =  
  16.                     new FileDescriptorInfo(fileIds[i], fileFds[i], fileAutoClose[i]);  
  17.         }  
  18.         assert clientContext != 0;  
  19.   
  20.         int callbackType = CALLBACK_FOR_UNKNOWN_PROCESS;  
  21.         boolean inSandbox = true;  
  22.         String processType = getSwitchValue(commandLine, SWITCH_PROCESS_TYPE);  
  23.         if (SWITCH_RENDERER_PROCESS.equals(processType)) {  
  24.             callbackType = CALLBACK_FOR_RENDERER_PROCESS;  
  25.         } else if (SWITCH_GPU_PROCESS.equals(processType)) {  
  26.             callbackType = CALLBACK_FOR_GPU_PROCESS;  
  27.         } else if (SWITCH_PPAPI_BROKER_PROCESS.equals(processType)) {  
  28.             inSandbox = false;  
  29.         }  
  30.   
  31.         ChildProcessConnection allocatedConnection = null;  
  32.         synchronized (ChildProcessLauncher.class) {  
  33.             if (inSandbox) {  
  34.                 allocatedConnection = sSpareSandboxedConnection;  
  35.                 sSpareSandboxedConnection = null;  
  36.             }  
  37.         }  
  38.         if (allocatedConnection == null) {  
  39.             allocatedConnection = allocateBoundConnection(context, commandLine, inSandbox);  
  40.             ......  
  41.         }  
  42.   
  43.         ......  
  44.         triggerConnectionSetup(allocatedConnection, commandLine, childProcessId, filesToBeMapped,  
  45.                 callbackType, clientContext);  
  46.         ......  
  47.     }  
  48.   
  49.     ......  
  50. }  

       这个函数定义在文件external/chromium_org/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncher.java中。

       ChildProcessLauncher类的成员函数start首先将参数fileIds、fileFds和fileAutoClose描述的文件描述符信息封装在一个FileDescriptorInfo数组中。记住 ,这里面包含了一个ID值为kPrimaryIPCChannel的文件描述符,该文件描述符描述的是用作IPC通信通道的一个UNIX Socket的客户端文件描述符。

       接下来,ChildProcessLauncher类的成员函数start判断要启动的子进程需不需要运行在沙箱中。ChildProcessLauncher类负责启动所有的Browser子进程,包括:

       1. Render进程;

       2. GPU进程;

       3. Plugin进程;

       4. Broker进程。

       其中,前面三种进程都是需要运行在沙箱里面的。这里所谓的沙箱,就是一个android:isolatedProcess属性被设置为true的Service进程,它不具有任何宿主应用程序申请的权限,这一点我们在前面Chromium多进程架构简要介绍和学习计划一文有提及到。

       对于第四种进程,它不是运行在沙箱里面的,这意味着它具有宿主应用程序申请的权限。什么情况下Browser进程会启动一个Broker进程呢? Chromium给Pepper Plugin提供了一个PPB_BrokerTrusted接口,通过该接口的成员函数Connect可以请求Browser进程创建一个Broker进程。由于Broker进程是一个具有特权的进程,因此,一个Pepper Plugin在调用PPB_BrokerTrusted接口的成员函数Connect的时候,Chromium会弹出一个infobar提示用户,只有用户同意的情况下,才允许请求Browser进程创建一个Broker进程。Broker进程同样会加载请求启动它的Pepper Plugin对应的模块文件,但是会调用不同的入口点函数。Broker进程启动完成之后,Pepper Plugin可以通过PPB_BrokerTrusted接口的GetHandle获得一个用来与Broker进程执行IPC的UNIX Socket文件描述符。通过该UNIX Socket文件描述符,Pepper Plugin就可以请求Broker进程执行一些特权操作了,这样就可以绕开Pepper Plugin由于运行在沙箱不能执行特权操作的问题。注意,这一切都是在用户允许的前提下才能够进行的。

       ChildProcessLauncher类的成员函数start通过获取命令行参数里面的SWITCH_PROCESS_TYPE选项确定要启动的子进程的类型,从而确定是否要将它运行在沙箱中。当要启动的子进程要运行沙箱的时候,本地变量inSandbox的值就会被设置为true。

       ChildProcessLauncher类提供了一个静态成员函数warmUp,Browser进程可以调用它提前启动一个运行在沙箱中的Service进程,并且获得该Service进程的一个ServiceConnection对象,该ServiceConnection对象封装在一个ChildProcessConnectionImpl对象中,并且该ChildProcessConnectionImpl对象保存在ChildProcessLauncher类的静态成员变量sSpareSandboxedConnection中。

       在要启动的子进程需要运行在沙箱的情况下,ChildProcessLauncher类的成员函数start尝试复用之前预先启动的子进程,因为这样可以加载子进程的启动过程。注意,一旦该预先启动的子进程被复用了,ChildProcessLauncher类的静态成员变量sSpareSandboxedConnection的值就被设置为null,表示它所描述的子进程已经被复用过了。

       不过,目前还没有在源码里面发现Browser进程有使用上述的warm up机制,因此,ChildProcessLauncher类的成员函数start接下来会调用另外一个静态成员函数allocateBoundConnection启动一个Service进程,并且调用静态成员函数triggerConnectionSetup将该Service进程的启动参数保存起来,以便等该Service进程启动完成之后,使用保存起来的参数对Service进程进行初始化。因此,接下来我们就分别分析ChildProcessLauncher类的静态成员函数allocateBoundConnection和triggerConnectionSetup。

       ChildProcessLauncher类的静态成员函数allocateBoundConnection的实现如下所示:

 

[java] view plaincopy

  1. public class ChildProcessLauncher {  
  2.     ......  
  3.   
  4.     private static ChildProcessConnection allocateBoundConnection(Context context,  
  5.             String[] commandLine, boolean inSandbox) {  
  6.         ChromiumLinkerParams chromiumLinkerParams = getLinkerParamsForNewConnection();  
  7.         ChildProcessConnection connection =  
  8.                 allocateConnection(context, inSandbox, chromiumLinkerParams);  
  9.         if (connection != null) {  
  10.             connection.start(commandLine);  
  11.         }  
  12.         return connection;  
  13.     }  
  14.   
  15.     ......  
  16. }  

       这个函数定义在文件external/chromium_org/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncher.java中。

 

       ChildProcessLauncher类的静态成员函数allocateBoundConnection首先调用静态成员函数getLinkerParamsForNewConnection获得Chromium相关的库链接信息,并且保存在一个ChromiumLinkerParams对象中,接下来再调用成员函数allocateConnection创建一个ChildProcessConnection对象,并且通过该ChildProcessConnection对象的成员函数start启动一个Service进程。

       关于Chromium相关库的链接信息,涉及到Chromium相关库的加载机制,以后我们分析WebView的加载过程时,再详细分析。接下来我们就主要分析ChildProcessLauncher类的静态成员函数allocateConnection的实现,如下所示:

 

[java] view plaincopy

  1. public class ChildProcessLauncher {  
  2.     ......  
  3.   
  4.     private static ChildProcessConnection allocateConnection(Context context,  
  5.             boolean inSandbox, ChromiumLinkerParams chromiumLinkerParams) {  
  6.         ChildProcessConnection.DeathCallback deathCallback =  
  7.             new ChildProcessConnection.DeathCallback() {  
  8.                 @Override  
  9.                 public void onChildProcessDied(ChildProcessConnection connection) {  
  10.                     if (connection.getPid() != 0) {  
  11.                         stop(connection.getPid());  
  12.                     } else {  
  13.                         freeConnection(connection);  
  14.                     }  
  15.                 }  
  16.             };  
  17.         sConnectionAllocated = true;  
  18.         return getConnectionAllocator(inSandbox).allocate(context, deathCallback,  
  19.                 chromiumLinkerParams);  
  20.     }  
  21.   
  22.     ......  
  23. }  

       这个函数定义在文件external/chromium_org/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncher.java中。

 

       ChildProcessLauncher类的静态成员函数allocateConnection首先创建了一个ChildProcessConnection.DeathCallback对象,该ChildProcessConnection.DeathCallback对象是用来监控接下来要启动的Service进程的生命周期的。一旦要启动的Service进程退出,该ChildProcessConnection.DeathCallback对象的成员函数onChildProcessDied就会被调用,并且用来执行清理工作。

       ChildProcessLauncher类的静态成员函数allocateConnection接下来调用静态成员函数getConnectionAllocator获得一个ChildConnectionAllocator对象,并且通过该ChildConnectionAllocator对象的成员函数allocate获得一个ChildProcessConnection对象。

       ChildProcessLauncher类的静态成员函数getConnectionAllocator的实现如下所示:

 

[java] view plaincopy

  1. public class ChildProcessLauncher {  
  2.     ......  
  3.   
  4.     private static final ChildConnectionAllocator sSandboxedChildConnectionAllocator =  
  5.             new ChildConnectionAllocator(true);  
  6.     private static final ChildConnectionAllocator sPrivilegedChildConnectionAllocator =  
  7.             new ChildConnectionAllocator(false);  
  8.       
  9.     ......  
  10.   
  11.     private static ChildConnectionAllocator getConnectionAllocator(boolean inSandbox) {  
  12.         return inSandbox ?  
  13.                 sSandboxedChildConnectionAllocator : sPrivilegedChildConnectionAllocator;  
  14.     }  
  15.   
  16.     ......  
  17. }  

       这个函数定义在文件external/chromium_org/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncher.java中。

 

       ChildProcessLauncher类的静态成员函数getConnectionAllocator根据参数inSandBox值的不同返回不同的ChildConnectionAllocator对象。当参数inSandBox等于true的时候,返回的是静态成员变量sSandboxedChildConnectionAllocator描述的ChildConnectionAllocator对象,而参数inSandBox等于true的时候,返回的是静态成员变量sPrivilegedChildConnectionAllocator描述的ChildConnectionAllocator对象。

       其中,ChildProcessLauncher类的静态成员变量sSandboxedChildConnectionAllocator描述的ChildConnectionAllocator对象用来描述运行在沙箱中的Service进程,而ChildProcessLauncher类的静态成员变量sPrivilegedChildConnectionAllocator描述的ChildConnectionAllocator对象用来描述非运行在沙箱中的Service进程。

       ChildConnectionAllocator对象的创建过程,即ChildConnectionAllocator类的构造函数的实现,如下所示:

 

[java] view plaincopy

  1. public class ChildProcessLauncher {  
  2.     ......  
  3.   
  4.     private static class ChildConnectionAllocator {  
  5.         private final ChildProcessConnection[] mChildProcessConnections;  
  6.         ......  
  7.         private final ArrayList<Integer> mFreeConnectionIndices;  
  8.         private final Object mConnectionLock = new Object();  
  9.   
  10.         private Class<? extends ChildProcessService> mChildClass;  
  11.         private final boolean mInSandbox;  
  12.   
  13.         public ChildConnectionAllocator(boolean inSandbox) {  
  14.             int numChildServices = inSandbox ?  
  15.                     MAX_REGISTERED_SANDBOXED_SERVICES : MAX_REGISTERED_PRIVILEGED_SERVICES;  
  16.             mChildProcessConnections = new ChildProcessConnectionImpl[numChildServices];  
  17.             mFreeConnectionIndices = new ArrayList<Integer>(numChildServices);  
  18.             for (int i = 0; i < numChildServices; i++) {  
  19.                 mFreeConnectionIndices.add(i);  
  20.             }  
  21.             setServiceClass(inSandbox ?  
  22.                     SandboxedProcessService.class : PrivilegedProcessService.class);  
  23.             mInSandbox = inSandbox;  
  24.         }  
  25.   
  26.         public void setServiceClass(Class<? extends ChildProcessService> childClass) {  
  27.             mChildClass = childClass;  
  28.         }  
  29.   
  30.         ......  
  31.     }  
  32.   
  33.     ......  
  34. }  

       这个函数定义在文件external/chromium_org/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncher.java中。

 

       Browser进程可以启动的最大运行在沙箱中和非运行在沙箱中的Service进程的数量分别为MAX_REGISTERED_SANDBOXED_SERVICES和MAX_REGISTERED_PRIVILEGED_SERVICES,这两个值分别设置为13和3,这意味着Browser进程不能无限地启动Service进程。

       ChildConnectionAllocator类的构造函数首先根据允许启动的Service进程的最大数量创建了一个ChildProcessConnectionImpl数组,并且保存在成员变量mChildProcessConnections中。注意,这时候创建的ChildProcessConnectionImpl数组是空的。接下来又创建了一个ArrayList对象,保存在另外一个成员变量mFreeConnectionIndices中,用来描述上述ChildProcessConnectionImpl数组的哪些元素是空的,也就是没有对应的ChildProcessConnectionImpl对象。

       最后,ChildConnectionAllocator类的构造函数调有另外一个成员函数setServiceClass设置要启动的Service进程所要运行的Service类,并且该Service类保存在ChildConnectionAllocator类的成员变量mChildClass中。从这里就可以看到,对于运行在沙箱中的Service进程,它所要运行的Service类为SandboxedProcessService,而对于非运行在沙箱中的Service进程,它所要运行的Service类为PrivilegedProcessService。

       在我们这个情景中,我们要启动的是一个运行在沙箱中的Render进程,因此,该Render进程要运行的Service类为SandboxedProcessService。

       回到ChildProcessLauncher类的静态成员函数allocateConnection中,它根据参数inSandbox获得了一个对应的ChildConnectionAllocator对象之后,就调用它的成员函数allocate获取一个ChildProcessConnectionImpl对象,以便可以通过该ChildProcessConnectionImpl对象启动一个Service进程。

       ChildConnectionAllocator类的成员函数allocate的实现如下所示:

 

[java] view plaincopy

  1. public class ChildProcessLauncher {  
  2.     ......  
  3.   
  4.     private static class ChildConnectionAllocator {  
  5.         ......  
  6.   
  7.         public ChildProcessConnection allocate(  
  8.                 Context context, ChildProcessConnection.DeathCallback deathCallback,  
  9.                 ChromiumLinkerParams chromiumLinkerParams) {  
  10.             synchronized (mConnectionLock) {  
  11.                 if (mFreeConnectionIndices.isEmpty()) {  
  12.                     Log.w(TAG, "Ran out of service.");  
  13.                     return null;  
  14.                 }  
  15.                 int slot = mFreeConnectionIndices.remove(0);  
  16.                 assert mChildProcessConnections[slot] == null;  
  17.                 mChildProcessConnections[slot] = new ChildProcessConnectionImpl(context, slot,  
  18.                         mInSandbox, deathCallback, mChildClass, chromiumLinkerParams);  
  19.                 return mChildProcessConnections[slot];  
  20.             }  
  21.         }  
  22.   
  23.         ......  
  24.     }  
  25.   
  26.     ......  
  27. }  

      这个函数定义在文件external/chromium_org/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncher.java中。

 

      ChildConnectionAllocator类的成员函数allocate首先通过成员变量mFreeConnectionIndices描述的一个ArrayList对象找到一个slot,该slot对应另外一个成员变量mChildProcessConnections描述的一个ChildProcessConnectionImpl数组的一个位置,该位置还没有一个对应的ChildProcessConnectionImpl对象。

      ChildConnectionAllocator类的成员函数allocate接下来就创建一个ChildProcessConnectionImpl对象,并且将该ChildProcessConnectionImpl对象保存在成员变量mChildProcessConnections描述的一个ChildProcessConnectionImpl数组的slot位置中,表示该位置已经被初始化。相应地,成员变量mFreeConnectionIndices描述的ArrayList对象将描述slot位置的元素移除,表示该slot已经被占用。

      最后,前面创建的ChildProcessConnectionImpl对象返回给了调用者,即ChildProcessLauncher类的静态成员函数allocateConnection,后者又将获得的ChildProcessConnectionImpl对象返回给了ChildProcessLauncher类的静态成员函数allocateBoundConnection,最终ChildProcessLauncher类的静态成员函数allocateBoundConnection调用了该ChildProcessConnectionImpl对象的成员函数start,用来启动一个Service进程。

      在分析ChildProcessConnectionImpl类的成员函数start之前,我们首先分析ChildProcessConnectionImpl对象的创建过程,即它的构造函数的实现,如下所示:

 

[java] view plaincopy

  1. public class ChildProcessConnectionImpl implements ChildProcessConnection {  
  2.     private final Context mContext;  
  3.     private final int mServiceNumber;  
  4.     ......  
  5.     private final Class<? extends ChildProcessService> mServiceClass;  
  6.     ......  
  7.     private ChildServiceConnection mInitialBinding = null;  
  8.     ......  
  9.   
  10.     ChildProcessConnectionImpl(Context context, int number, boolean inSandbox,  
  11.             ChildProcessConnection.DeathCallback deathCallback,  
  12.             Class<? extends ChildProcessService> serviceClass,  
  13.             ChromiumLinkerParams chromiumLinkerParams) {  
  14.         mContext = context;  
  15.         mServiceNumber = number;  
  16.         ......  
  17.         mServiceClass = serviceClass;  
  18.         ......  
  19.         mInitialBinding = new ChildServiceConnection(Context.BIND_AUTO_CREATE);  
  20.         ......  
  21.     }  
  22.   
  23.     ......  
  24. }  

      这个函数定义在文件external/chromium_org/content/public/android/java/src/org/chromium/content/browser/ChildProcessConnectionImpl.java中。

 

      ChildProcessConnectionImpl类的构造函数做的最重要的事情是将它要启动的Service进程要运行的Service记录在成员变量mServiceClass中,以及创建一个ChildServiceConnection对象,保存在成员变量mInitialBinding中。在接下来启动Service进程的过程,需要使用到这两个成员变量。

       接下来我们就继续分析ChildProcessConnectionImpl类的成员函数start的实现,以便可以了解一个Service进程的启动过程,如下所示:

 

[java] view plaincopy

  1. public class ChildProcessConnectionImpl implements ChildProcessConnection {  
  2.     ......  
  3.   
  4.     @Override  
  5.     public void start(String[] commandLine) {  
  6.         synchronized (mLock) {  
  7.             ......  
  8.   
  9.             if (!mInitialBinding.bind(commandLine)) {  
  10.                 ......  
  11.             } else {  
  12.                 .....  
  13.             }  
  14.             ......  
  15.         }  
  16.     }  
  17.   
  18.     ......  
  19. }  

      这个函数定义在文件external/chromium_org/content/public/android/java/src/org/chromium/content/browser/ChildProcessConnectionImpl.java中。

 

      ChildProcessConnectionImpl类的成员函数start调用了成员变量mInitialBinding描述的一个ChildServiceConnection对象的成员函数bind启动一个Service进程,它的实现如下所示:

 

[java] view plaincopy

  1. public class ChildProcessConnectionImpl implements ChildProcessConnection {  
  2.     private final Context mContext;  
  3.     ......  
  4.     private final int mServiceNumber;  
  5.     ......  
  6.     private final Class<? extends ChildProcessService> mServiceClass;  
  7.     ......  
  8.   
  9.     private class ChildServiceConnection implements ServiceConnection {  
  10.         private boolean mBound = false;  
  11.         ......  
  12.   
  13.         private Intent createServiceBindIntent() {  
  14.             Intent intent = new Intent();  
  15.             intent.setClassName(mContext, mServiceClass.getName() + mServiceNumber);  
  16.             intent.setPackage(mContext.getPackageName());  
  17.             return intent;  
  18.         }  
  19.   
  20.         ......  
  21.   
  22.         boolean bind(String[] commandLine) {  
  23.             if (!mBound) {  
  24.                 .....  
  25.                 final Intent intent = createServiceBindIntent();  
  26.                 if (commandLine != null) {  
  27.                     intent.putExtra(EXTRA_COMMAND_LINE, commandLine);  
  28.                 }  
  29.                 ......  
  30.                 mBound = mContext.bindService(intent, this, mBindFlags);  
  31.                 ......  
  32.             }  
  33.             return mBound;  
  34.         }  
  35.   
  36.         ......  
  37.     }  
  38.   
  39.     ......  
  40. }  

       这个函数定义在文件external/chromium_org/content/public/android/java/src/org/chromium/content/browser/ChildProcessConnectionImpl.java中。

       ChildServiceConnection类的成员函数bind主要就是调用外部类ChildProcessConnectionImpl的成员变量mContext描述的一个Context对象的成员函数bindService启动一个Service,并且在参数commandLine的值不等于null的情况下,将其以EXTRA_COMMND_LINE为键值,保存在用来启动Service的一个Intent中,以便可以将参数commadLine传递给要启动的Service。这里我们需要记住一点,参数commandLine此时包含了一个switches::kProcessType和一个switches:kProcessChannelID选项,前者的值等于switches::kRendererProcess,后者的值为一个UNIX Socket名称。它们分别是在前面分析的RenderProcessHostImpl类的成员函数AppendRendererCommandLine和Init中设置的。

       注意,要启动的Service由外部类ChildProcessConnectionImpl的成员变量mServiceClass确定。从前面的分析可以知道,当启动的Service以沙箱模式运行独立的进程中时,ChildProcessConnectionImpl的成员变量mServiceClass描述的类是SandboxedProcessService。

       但是从ChildServiceConnection类的成员函数createServiceBindIntent可以知道,实际启动的类并不是SandboxedProcessService,而是一个SandboxedProcessService0、SandboxedProcessService1或者SandboxedProcessService2之类的类。这意味Chromium应用程序要自己定义一系列的SandboxedProcessService<N>类,并且要求这些类像SandboxedProcessService类一样,是从ChildProcessService类继承下来的,并且它们在AndroidManifest.xml文件配置为在单独的进程中启动,以及将android:isolatedProcess属性设置为true。至于这一系列SandboxedProcessService<N>类的个数,就等于前面提到的ChildProcessLauncher类的静态成员变量MAX_REGISTERED_SANDBOXED_SERVICES的值,即13。

       这一步执行完成后,Browser进程就会等待指定的Service启动成功。我们知道,当我们通过Context.bindService启动一个Service的时候,需要指定一个ServiceConnection对象。当Service启动起来之后,指定的ServiceConnection对象的成员函数onServiceConnected就会被调用。在我们这个场景中,就是ChildServiceConnection类的成员函数onServiceConnected被调用。

       在分析ChildServiceConnection类的成员函数onServiceConnected的实现之前,我们先回到前面分析的ChildProcessLauncher类的成员函数start,它接下来会调用另外一个成员函数triggerConnectionSetup将正在启动的Service进程的启动参数保存起来,如下所示:

 

[java] view plaincopy

  1. public class ChildProcessLauncher {  
  2.     ......  
  3.   
  4.     @VisibleForTesting  
  5.     static void triggerConnectionSetup(  
  6.             final ChildProcessConnection connection,  
  7.             String[] commandLine,  
  8.             int childProcessId,  
  9.             FileDescriptorInfo[] filesToBeMapped,  
  10.             int callbackType,  
  11.             final long clientContext) {  
  12.         ChildProcessConnection.ConnectionCallback connectionCallback =  
  13.                 new ChildProcessConnection.ConnectionCallback() {  
  14.                     @Override  
  15.                     public void onConnected(int pid) {  
  16.                         ......  
  17.                         if (clientContext != 0) {  // Will be 0 in Java instrumentation tests.  
  18.                             nativeOnChildProcessStarted(clientContext, pid);  
  19.                         }  
  20.                     }  
  21.                 };  
  22.   
  23.         ......  
  24.   
  25.         connection.setupConnection(commandLine,  
  26.                                    filesToBeMapped,  
  27.                                    createCallback(childProcessId, callbackType),  
  28.                                    connectionCallback,  
  29.                                    Linker.getSharedRelros());  
  30.     }  
  31.   
  32.     ......  
  33. }  

       这个函数定义在文件external/chromium_org/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncher.java中。

 

       ChildProcessLauncher类的成员函数triggerConnectionSetup首先创建了一个ChildProcessConnection.ConnectionCallback对象,并且传递给接下来要调用的参数connection描述的一个ChildProcessConnection对象的成员函数setupConnection。这样当前面要启动的Service启动成功之后,该ChildProcessConnection.ConnectionCallback对象的成员函数onConnected就会被调用。这时候ChildProcessConnection.ConnectionCallback类的成员函数onConnected就会通过JNI方法nativeOnChildProcessStarted通知Browser进程的Native层,它之前要启动的Render进程已经启动完成。

       从前面的调用过程可以知道,参数connection指向的实际上是一个ChildProcessConnectionImpl对象,它的成员函数setupConnection的实现如下所示:

 

[java] view plaincopy

  1. public class ChildProcessConnectionImpl implements ChildProcessConnection {  
  2.     ......  
  3.   
  4.     private ConnectionParams mConnectionParams;  
  5.     ......  
  6.   
  7.     private ChildProcessConnection.ConnectionCallback mConnectionCallback;  
  8.     ......  
  9.   
  10.     @Override  
  11.     public void setupConnection(  
  12.             String[] commandLine,  
  13.             FileDescriptorInfo[] filesToBeMapped,  
  14.             IChildProcessCallback processCallback,  
  15.             ConnectionCallback connectionCallback,  
  16.             Bundle sharedRelros) {  
  17.         synchronized (mLock) {  
  18.             ......  
  19.             mConnectionCallback = connectionCallback;  
  20.             mConnectionParams = new ConnectionParams(  
  21.                     commandLine, filesToBeMapped, processCallback, sharedRelros);  
  22.             // Run the setup if the service is already connected. If not, doConnectionSetupLocked()  
  23.             // will be called from onServiceConnected().  
  24.             if (mServiceConnectComplete) {  
  25.                 doConnectionSetupLocked();  
  26.             }  
  27.             ......  
  28.         }  
  29.     }  
  30.   
  31.     ......  
  32. }  

       这个函数定义在文件external/chromium_org/content/public/android/java/src/org/chromium/content/browser/ChildProcessConnectionImpl.java中。

 

       ChildProcessConnectionImpl类的成员函数setupConnection主要做了三件事情。第一件事情是将参数connectionCallback描述的一个ConnectionCallback对象保存在成员变量mConnectionCallback中,这就是为了等到要启动的Service启动成功之后,可以调用该ConnectionCallback对象的成员函数onConnected,以便Browser进程知道它想要启动的Render进程已经启动完成了。第二件事情是将其余的参数封装在一个ConnectionParams对象中,并且这个ConnectionParams对象会保存在ChildProcessConnectionImpl类的成员变量mConnectionParams中。注意,这里有一个重要的参数filesToBeMapped,它里面包含了一个以kPrimaryIPCChannel为键值的UNIX Socket的Client端文件描述符。第三件事情是判断成员变量mServiceConnectComplete的值是否等于true。如果等于true,那么就表示要启动的Service已经启动完成了。在这种情况下,ChildProcessConnectionImpl类的成员函数setupConnection就会调用另外一个成员函数doConnectionSetupLocked对启动起来的Service进程做一些设置工作。

       我们假设在ChildProcessConnectionImpl类的成员函数setupConnection的调用期间,要启动的Service还没有启动完成,于是Browser进程就会通过前面提到的ServiceConnection类的成员函数onServiceConnected获得Service启动完成通知。

       ServiceConnection类的成员函数onServiceConnected的实现如下所示:

 

[java] view plaincopy

  1. public class ChildProcessConnectionImpl implements ChildProcessConnection {  
  2.     ......  
  3.   
  4.     private IChildProcessService mService = null;  
  5.     ......  
  6.   
  7.     private ConnectionParams mConnectionParams;  
  8.     ......  
  9.   
  10.     private class ChildServiceConnection implements ServiceConnection {  
  11.         ......  
  12.   
  13.         @Override  
  14.         public void onServiceConnected(ComponentName className, IBinder service) {  
  15.             synchronized (mLock) {  
  16.                 ......  
  17.                 mServiceConnectComplete = true;  
  18.                 mService = IChildProcessService.Stub.asInterface(service);  
  19.                 // Run the setup if the connection parameters have already been provided. If not,  
  20.                 // doConnectionSetupLocked() will be called from setupConnection().  
  21.                 if (mConnectionParams != null) {  
  22.                     doConnectionSetupLocked();  
  23.                 }  
  24.                 ......  
  25.             }  
  26.         }  
  27.   
  28.         ......  
  29.     }  
  30.   
  31.     ......  
  32. }  

       这个函数定义在文件external/chromium_org/content/public/android/java/src/org/chromium/content/browser/ChildProcessConnectionImpl.java中。

 

       ServiceConnection类的成员函数onServiceConnected被调用的时候,传递进来的第二个参数service是一个类型为IChildProcessService的Binder代理接口,与它对应的Binder服务运行在已经启动起来的Service进程中。有了这个Binder代理接口之后,Browser进程就可以与启动起来的Service进程进行IPC了。

       ServiceConnection类的成员函数onServiceConnected将参数service描述的一个类型为IChildProcessService的Binder代理接口保存在外部类ChildProcessConnectionImpl的成员变量mService之后,就会继续判断外部类ChildProcessConnectionImpl的成员变量mConnectionParams的值是否等于null。如果不等于null,那么就说明它封装了一些参数,这些参数需要用来对刚刚启动起来的Service进程进行设置。在这种情况下,就会调用外部类ChildProcessConnectionImpl的成员函数doConnectionSetupLocked对刚刚启动起来的Service进程进行设置。

      ChildProcessConnectionImpl类的成员函数doConnectionSetupLocked的实现如下所示:

 

[java] view plaincopy

  1. public class ChildProcessConnectionImpl implements ChildProcessConnection {  
  2.     ......  
  3.   
  4.     private void doConnectionSetupLocked() {  
  5.         ......  
  6.   
  7.         Bundle bundle = new Bundle();  
  8.         bundle.putStringArray(EXTRA_COMMAND_LINE, mConnectionParams.mCommandLine);  
  9.   
  10.         FileDescriptorInfo[] fileInfos = mConnectionParams.mFilesToBeMapped;  
  11.         ParcelFileDescriptor[] parcelFiles = new ParcelFileDescriptor[fileInfos.length];  
  12.         for (int i = 0; i < fileInfos.length; i++) {  
  13.             ......  
  14.             String idName = EXTRA_FILES_PREFIX + i + EXTRA_FILES_ID_SUFFIX;  
  15.             String fdName = EXTRA_FILES_PREFIX + i + EXTRA_FILES_FD_SUFFIX;  
  16.             if (fileInfos[i].mAutoClose) {  
  17.                 // Adopt the FD, it will be closed when we close the ParcelFileDescriptor.  
  18.                 parcelFiles[i] = ParcelFileDescriptor.adoptFd(fileInfos[i].mFd);  
  19.             } else {  
  20.                 try {  
  21.                     parcelFiles[i] = ParcelFileDescriptor.fromFd(fileInfos[i].mFd);  
  22.                 } catch (IOException e) {  
  23.                     .....  
  24.                 }  
  25.   
  26.             }  
  27.             bundle.putParcelable(fdName, parcelFiles[i]);  
  28.             bundle.putInt(idName, fileInfos[i].mId);  
  29.         }  
  30.   
  31.         try {  
  32.             mPid = mService.setupConnection(bundle, mConnectionParams.mCallback);  
  33.             ......  
  34.         } catch (android.os.RemoteException re) {  
  35.             ......  
  36.         }  
  37.           
  38.         ......  
  39.   
  40.         if (mConnectionCallback != null) {  
  41.             mConnectionCallback.onConnected(mPid);  
  42.         }  
  43.         ......  
  44.     }  
  45.   
  46.     ......  
  47. }  

       这个函数定义在文件external/chromium_org/content/public/android/java/src/org/chromium/content/browser/ChildProcessConnectionImpl.java中。

 

       ChildProcessConnectionImpl类的成员函数doConnectionSetupLocked主要做的事情就是将Browser进程指定的命令行参数和文件描述符等信息,通过成员变量mService描述的一个类型为IChildProcessService的Binder代理接口的成员函数setupConnection,传递给刚刚启动起来的Service进程,也就是一个Render进程。 

       ChildProcessConnectionImpl类的成员函数doConnectionSetupLocked最后还会判断成员变量mConnectionCallback的值是否等于null。如果不等于null,那么该成员变量就指向了一个ChildProcessConnection.ConnectionCallback对象。在这种情况下,需要调用上述ChildProcessConnection.ConnectionCallback对象的成员函数onConnected,以便通知ChildProcessLauncher类,它所要启动的Service进程已经启动完毕。

       前面提到,与ChildProcessConnectionImpl类的成员变量mService所描述的Binder代理对应的Binder服务运行在刚刚启动完成的Service进程中,因此,当我们调用该Binder代理的成员函数setupConnection的时候,实际上调用的是运行在刚刚启动的Service进程的一个Binder服务的成员函数setupConnection。现在,我们就将目光转向Service进程的启动过程,也就是Render进程启动过程的第二部分子过程,如下所示:

图8 Render进程启动的第二部分子过程

       我们知道,一个Service在启动的时候,它的成员函数onCreate就会被调用。在我们这个情景中,启动的Service是一个SandboxedProcessService<N>,它是从ChildProcessService继承下来的,我们假设SandboxedProcessService<N>没有重写父类ChildProcessService的成员函数onCreate,那么当SandboxedProcessService<N>在新启动的Service进程中加载的过程中,ChildProcessService类的成员函数onCreate就会被调用。

       ChildProcessService类的成员函数onCreate的实现如下所示:

 

[java] view plaincopy

  1. public class ChildProcessService extends Service {  
  2.     ......  
  3.   
  4.     @Override  
  5.     public void onCreate() {  
  6.         ......  
  7.   
  8.         mMainThread = new Thread(new Runnable() {  
  9.             @Override  
  10.             public void run()  {  
  11.                 try {  
  12.                     ......  
  13.   
  14.                     try {  
  15.                         LibraryLoader.loadNow(getApplicationContext(), false);  
  16.                     } catch (ProcessInitException e) {  
  17.                         ......  
  18.                     }  
  19.   
  20.                     synchronized (mMainThread) {  
  21.                         while (mCommandLineParams == null) {  
  22.                             mMainThread.wait();  
  23.                         }  
  24.                     }  
  25.                     LibraryLoader.initialize(mCommandLineParams);  
  26.   
  27.                     synchronized (mMainThread) {  
  28.                         ......  
  29.                         while (mFileIds == null) {  
  30.                             mMainThread.wait();  
  31.                         }  
  32.                     }  
  33.                     ......  
  34.                     int[] fileIds = new int[mFileIds.size()];  
  35.                     int[] fileFds = new int[mFileFds.size()];  
  36.                     for (int i = 0; i < mFileIds.size(); ++i) {  
  37.                         fileIds[i] = mFileIds.get(i);  
  38.                         fileFds[i] = mFileFds.get(i).detachFd();  
  39.                     }  
  40.                     ......  
  41.   
  42.                     nativeInitChildProcess(sContext.get().getApplicationContext(),  
  43.                             ChildProcessService.this, fileIds, fileFds,  
  44.                             mCpuCount, mCpuFeatures);  
  45.                     ContentMain.start();  
  46.                     ......  
  47.                 } catch (InterruptedException e) {  
  48.                     ......  
  49.                 } catch (ProcessInitException e) {  
  50.                     ......  
  51.                 }  
  52.             }  
  53.         }, MAIN_THREAD_NAME);  
  54.         mMainThread.start();  
  55.     }  
  56.   
  57.     ......  
  58. }  

       这个函数定义在文件external/chromium_org/content/public/android/java/src/org/chromium/content/app/ChildProcessService.java。

 

       ChildProcessService类的成员函数onCreate启动了一个线程,该线程由成员变量mMainThread描述,它主要做了以下几件事情:

       1. 调用LibraryLoader类的静态成员函数loadNow加载了Chromium相关的so库。

       2. 等待成员变量mCommandLineParams的值不等于null。当该成员变量的值不等于null的时候,它就指向了一个String数组。该String数组描述的是当前进程的命令行参数,因此,它会通过LibraryLoader类的静态成员函数initialize设置为当前进程的命令参数。

       3. 等待成员变量mFileIds的值不等于null。当该成员变量的值不等于null的时候,另外一个成员变量mFileIds的值也不等于null,这时候它们描述了一系列的文件描述符及对应的ID。 这些文件描述符及其对应的ID会通过JNI方法nativeInitChildProcess传递给Native层。

       4. 调用ContentMain类的静态成员函数start启动Native层的Chromium。

       ChildProcessService类的成员变量mCommandLineParams、mFileIds和mFileIds的值开始的时候都是等于null的,那么它们是什么时候被设置的呢?前面提到,当Browser进程获得了一个可以用来与刚刚启动的Service进程进行Binder IPC的类型为IChildProcessService的Binder代理之后,就会调用该Binder代理的成员函数setupConnection,这会导致运行在刚刚启动的Service进程中的一个对应的Binder服务的成员函数setupConnection被调用。

       通过ChildProcessService类的成员函数onBind,我们可以看到上述的Bind服务是什么,如下所示:

 

[java] view plaincopy

  1. public class ChildProcessService extends Service {  
  2.     ......  
  3.   
  4.     @Override  
  5.     public IBinder onBind(Intent intent) {  
  6.         ......  
  7.   
  8.         return mBinder;  
  9.     }  
  10.   
  11.     ......  
  12. }  

      这个函数定义在文件external/chromium_org/content/public/android/java/src/org/chromium/content/app/ChildProcessService.java。

 

      从这里我们就可以看到,ChildProcessService类的成员函数onBind返回的Binder服务就即为上述提到的Binder服务,它由成员变量mBinder指定,如下所示:

 

[java] view plaincopy

  1. public class ChildProcessService extends Service {  
  2.     ......  
  3.   
  4.     private final IChildProcessService.Stub mBinder = new IChildProcessService.Stub() {  
  5.         // NOTE: Implement any IChildProcessService methods here.  
  6.         @Override  
  7.         public int setupConnection(Bundle args, IChildProcessCallback callback) {  
  8.             synchronized (mMainThread) {  
  9.                 // Allow the command line to be set via bind() intent or setupConnection, but  
  10.                 // the FD can only be transferred here.  
  11.                 if (mCommandLineParams == null) {  
  12.                     mCommandLineParams = args.getStringArray(  
  13.                             ChildProcessConnection.EXTRA_COMMAND_LINE);  
  14.                 }  
  15.                 ......  
  16.                 mFileIds = new ArrayList<Integer>();  
  17.                 mFileFds = new ArrayList<ParcelFileDescriptor>();  
  18.                 for (int i = 0;; i++) {  
  19.                     String fdName = ChildProcessConnection.EXTRA_FILES_PREFIX + i  
  20.                             + ChildProcessConnection.EXTRA_FILES_FD_SUFFIX;  
  21.                     ParcelFileDescriptor parcel = args.getParcelable(fdName);  
  22.                     ......  
  23.                     mFileFds.add(parcel);  
  24.                     String idName = ChildProcessConnection.EXTRA_FILES_PREFIX + i  
  25.                             + ChildProcessConnection.EXTRA_FILES_ID_SUFFIX;  
  26.                     mFileIds.add(args.getInt(idName));  
  27.                 }  
  28.   
  29.                 mMainThread.notifyAll();  
  30.             }  
  31.             return Process.myPid();  
  32.         }  
  33.     }  
  34.   
  35.     ......  
  36. }  

       这个函数定义在文件external/chromium_org/content/public/android/java/src/org/chromium/content/app/ChildProcessService.java。

 

       从这里就可以看到,当ChildProcessService类的成员变量mBinder描述的Binder服务的成员函数setupConnection被调用的时候,它就会通过参数args获得Browser进程传递过来的命令行参数和文件描述符,分别保存在成员变量mCommandLineParams、mFileIds和mFileFds中,这时候这几个成员变量的值就不等于null。因此当最后ChildProcessService类的成员变量mMainThread描述的线程被唤醒之后,它就能继续往前执行,也就是执行前面提到的ChildProcessService类的JNI方法nativeInitChildProcess和ContentMain类的成员函数start。

       接下来我们就分别分析ChildProcessService类的JNI方法nativeInitChildProcess和ContentMain类的成员函数start的实现。

       ChildProcessService类的JNI方法nativeInitChildProcess同Native层的函数Java_com_android_org_chromium_content_app_ChildProcessService_nativeInitChildProcess实现,如下所示:

 

[cpp] view plaincopy

  1. __attribute__((visibility("default")))  
  2. void  
  3.     Java_com_android_org_chromium_content_app_ChildProcessService_nativeInitChildProcess(JNIEnv*  
  4.     env, jclass jcaller,  
  5.     jobject applicationContext,  
  6.     jobject service,  
  7.     jintArray extraFileIds,  
  8.     jintArray extraFileFds,  
  9.     jint cpuCount,  
  10.     jlong cpuFeatures) {  
  11.   return InitChildProcess(env, jcaller, applicationContext, service,  
  12.       extraFileIds, extraFileFds, cpuCount, cpuFeatures);  
  13. }  

      这个函数定义在文件out/target/product/generic/obj/GYP/shared_intermediates/content/jni/ChildProcessService_jni.h中。

 

      函数Java_com_android_org_chromium_content_app_ChildProcessService_nativeInitChildProcess调用了另外一个函数InitChildProcess来对当前进程执行一些初始化工作,后者的实现如下所示:

 

[cpp] view plaincopy

  1. void InitChildProcess(JNIEnv* env,  
  2.                       jclass clazz,  
  3.                       jobject context,  
  4.                       jobject service,  
  5.                       jintArray j_file_ids,  
  6.                       jintArray j_file_fds,  
  7.                       jint cpu_count,  
  8.                       jlong cpu_features) {  
  9.   std::vector<int> file_ids;  
  10.   std::vector<int> file_fds;  
  11.   JavaIntArrayToIntVector(env, j_file_ids, &file_ids);  
  12.   JavaIntArrayToIntVector(env, j_file_fds, &file_fds);  
  13.   
  14.   InternalInitChildProcess(  
  15.       file_ids, file_fds, env, clazz, context, service,  
  16.       cpu_count, cpu_features);  
  17. }  

      这个函数定义在文件external/chromium_org/content/app/android/child_process_service.cc中。

 

      函数InitChildProcess首先是将参数j_file_ids和j_file_fds描述的一系列文件描述符及其对应的ID取出来,并且分别保存在本地变量file_ids和file_fds描述的std::vector中,将这两个std::vector传递给另外一个函数InternalInitChildProcess,后者对上述文件描述符及其对应的ID的处理如下所示:

 

[cpp] view plaincopy

  1. void InternalInitChildProcess(const std::vector<int>& file_ids,  
  2.                               const std::vector<int>& file_fds,  
  3.                               JNIEnv* env,  
  4.                               jclass clazz,  
  5.                               jobject context,  
  6.                               jobject service_in,  
  7.                               jint cpu_count,  
  8.                               jlong cpu_features) {  
  9.   ......  
  10.   
  11.   // Register the file descriptors.  
  12.   // This includes the IPC channel, the crash dump signals and resource related  
  13.   // files.  
  14.   ......  
  15.   for (size_t i = 0; i < file_ids.size(); ++i)  
  16.     base::GlobalDescriptors::GetInstance()->Set(file_ids[i], file_fds[i]);  
  17.   
  18.   ......  
  19. }  

      这个函数定义在文件external/chromium_org/content/app/android/child_process_service.cc中。

 

      从这里就可以看到,函数InternalInitChildProcess将参数file_fds描述的文件描述符以参数file_ids描述的ID为键值,注册在当前进程的一个Global Descriptors中。这其中就包含了一个UNIX Socket的Client端文件描述符,该文件描述符的ID值为kPrimaryIPCChannel,并且该UNIX Socket就是Browser进程用来与它请求启动的进程,例如Render进程、GPU进程和Plugin进程,建立IPC通信通道使用的。

       前面我们提到,当我们通过IPC::SyncChannel类的静态成员函数Create创建一个Client端的IPC通信通道时,就会从Global Descriptors中取出ID值为kPrimaryIPCChannel的文件描述符,以便可以创建一个用来与Browser进程执行IPC的通信通道。

       这一步执行完成之后,接下来我们继续分析ContentMain类的静态成员函数start的实现,如下所示:

 

[cpp] view plaincopy

  1. public class ContentMain {  
  2.     ......  
  3.   
  4.     public static int start() {  
  5.         return nativeStart();  
  6.     }  
  7.   
  8.     ......  
  9.   
  10.     private static native int nativeStart();  
  11. }  

      这个函数定义在文件external/chromium_org/content/public/android/java/src/org/chromium/content/app/ContentMain.java中。

 

      从这里可以看到,ContentMain类的静态成员函数start调用JNI方法nativeStart启动Native层的Chromium。

      ContentMain类的JNI方法nativeStart由Native层的函数Java_com_android_org_chromium_content_app_ContentMain_nativeStart实现,如下所示:

 

[cpp] view plaincopy

  1. __attribute__((visibility("default")))  
  2. jint Java_com_android_org_chromium_content_app_ContentMain_nativeStart(JNIEnv*  
  3.     env, jclass jcaller) {  
  4.   return Start(env, jcaller);  
  5. }  

      这个函数定义在文件out/target/product/generic/obj/GYP/shared_intermediates/content/jni/ContentMain_jni.h中。

 

      函数Java_com_android_org_chromium_content_app_ContentMain_nativeStart调用了另外一个函数Start执行启动Chromium的工作,后者的实现如下所示:

 

[cpp] view plaincopy

  1. static jint Start(JNIEnv* env, jclass clazz) {  
  2.   ......  
  3.   
  4.   if (!g_content_runner.Get().get()) {  
  5.     ContentMainParams params(g_content_main_delegate.Get().get());  
  6.     g_content_runner.Get().reset(ContentMainRunner::Create());  
  7.     g_content_runner.Get()->Initialize(params);  
  8.   }  
  9.   return g_content_runner.Get()->Run();  
  10. }  

       这个函数定义在文件external/chromium_org/content/app/android/content_main.cc中。

       函数Start首先检查全局变量g_content_runner是否被初始化。如果还没有被初始化,那么就会调用ContentMainRunner类的静态成员函数Create创建一个ContentMainRunnerImpl对象,如下所示:

 

[cpp] view plaincopy

  1. ContentMainRunner* ContentMainRunner::Create() {  
  2.   return new ContentMainRunnerImpl();  
  3. }  

       这个函数定义在文件external/chromium_org/content/app/content_main_runner.cc中。

 

       ContentMainRunner类的静态成员函数Create返回的实际上是一个ContentMainRunnerImpl对象,因此,上述全局变量g_content_runner指向的是一个ContentMainRunnerImpl对象。

       回到函数Start中,创建了一个ContentMainRunnerImpl对象之后,接下来还会调用它的成员函数Initialize执行初始化工作,以及最后调用它的成员函数Run使得当前进程进入运行状态。

       ContentMainRunnerImpl类的成员函数Run的实现如下所示:

 

[cpp] view plaincopy

  1. class ContentMainRunnerImpl : public ContentMainRunner {  
  2.  public:  
  3.   ......  
  4.   
  5.   virtual int Run() OVERRIDE {  
  6.     ......  
  7.     const CommandLine& command_line = *CommandLine::ForCurrentProcess();  
  8.     std::string process_type =  
  9.           command_line.GetSwitchValueASCII(switches::kProcessType);  
  10.   
  11.     MainFunctionParams main_params(command_line);  
  12.     main_params.ui_task = ui_task_;  
  13.     ......  
  14.   
  15.     return RunNamedProcessTypeMain(process_type, main_params, delegate_);  
  16.   
  17.     ......  
  18.   }  
  19.   
  20.  ......  
  21. };  

      这个函数定义在文件external/chromium_org/content/app/content_main_runner.cc中。

 

      ContentMainRunnerImpl类的成员函数Run首先是获得当前进程的命令行参数,并且保存在本地变量comand_line中。从前面的分析可以知道,当前进程的命令行参数是从Browser进程传递过来的,它里面包含了一个kProcessType选项。在我们这个情景中,这个kProcessType选项的值等于kRendererProcess。也就是说,本地变量process_type的值等于kRendererProcess。

      最后,ContentMainRunnerImpl类的成员函数Run调用函数RunNamedProcessTypeMain使得当前进程进入运行状态,如下所示:

 

[cpp] view plaincopy

  1. int RunNamedProcessTypeMain(  
  2.     const std::string& process_type,  
  3.     const MainFunctionParams& main_function_params,  
  4.     ContentMainDelegate* delegate) {  
  5.   static const MainFunction kMainFunctions[] = {  
  6. #if !defined(CHROME_MULTIPLE_DLL_CHILD)  
  7.     { "",                            BrowserMain },  
  8. #endif  
  9. #if !defined(CHROME_MULTIPLE_DLL_BROWSER)  
  10. #if defined(ENABLE_PLUGINS)  
  11. #if !defined(OS_LINUX)  
  12.     { switches::kPluginProcess,      PluginMain },  
  13. #endif  
  14.     { switches::kWorkerProcess,      WorkerMain },  
  15.     { switches::kPpapiPluginProcess, PpapiPluginMain },  
  16.     { switches::kPpapiBrokerProcess, PpapiBrokerMain },  
  17. #endif  // ENABLE_PLUGINS  
  18.     { switches::kUtilityProcess,     UtilityMain },  
  19.     { switches::kRendererProcess,    RendererMain },  
  20.     { switches::kGpuProcess,         GpuMain },  
  21. #endif  // !CHROME_MULTIPLE_DLL_BROWSER  
  22.   };  
  23.   
  24.   ......  
  25.   
  26.   for (size_t i = 0; i < arraysize(kMainFunctions); ++i) {  
  27.     if (process_type == kMainFunctions[i].name) {  
  28.       if (delegate) {  
  29.         int exit_code = delegate->RunProcess(process_type,  
  30.             main_function_params);  
  31. #if defined(OS_ANDROID)  
  32.         // In Android's browser process, the negative exit code doesn't mean the  
  33.         // default behavior should be used as the UI message loop is managed by  
  34.         // the Java and the browser process's default behavior is always  
  35.         // overridden.  
  36.         if (process_type.empty())  
  37.           return exit_code;  
  38. #endif  
  39.         if (exit_code >= 0)  
  40.           return exit_code;  
  41.       }  
  42.       return kMainFunctions[i].function(main_function_params);  
  43.     }  
  44.   }  
  45.   
  46.   ......  
  47. }  

       这个函数定义在文件external/chromium_org/content/app/content_main_runner.cc中。

 

       在函数RunNamedProcessTypeMain内部,定义了一个类型为MainFunction的静态数组kMainFunctions。这个数组记录了每一个进程类型对应的运行入口点函数。例如,对于类型为switches::kRendererProcess的进程来说,即Render进程,它的运行入口点函数为RendererMain。又如,对于类型为switches::kGpuProcess的进程来说,即GPU进程,它的运行入口点函数为RendererMain。注意,Browser进程的类型是一个空字符串,因此它的运行入口点函数为BrowserMain。

       从前面的调用过程可以知道,参数delegate指向的ContentMainDelegate对象来自于ContentMainRunnerImpl类的成员变量delegate_。对于非Browser进程来说,ContentMainRunnerImpl类的成员变量delegate_的值是等于NULL的。对于Chromium浏览器来说,它的Browser进程中的ContentMainRunnerImpl类的成员变量delegate_指向的是一个ChromeMainDelegateChromeShellAndroid对象,而对于WebView来说,它的Browser进程中的ContentMainRunnerImpl类的成员变量delegate_指向的是一个AwMainDelegate对象。由于现在我们分析的是非Browser进程,因此,参数delegate的值就等于NULL。

       函数RunNamedProcessTypeMain通过遍历数组kMainFunctions,找到与参数process_type对应的进程运行入口点函数,并且调用它。不过,如果参数delegate的值不等于NULL,那么就会先调用它指向的一个ContentMainDelegate对象的成员函数RunProcess。只有当该ContentMainDelegate对象的成员函数RunProcess的返回值为负值的情况下,才会调用在数组中kMainFunctions中找到的运行入口点函数。

       但是,对于Android平台的Chromium的Browser进程来说,当它从参数delegate指向的ContentMainDelegate对象的成员函数RunProcess返回来之后,不管返回值是什么,都会直接退出,而不会执行数组kMainFunctions中对应的运行入口点函数。这是因为Android平台的Chromium的Browser进程即为Android应用程序的主进程,它在Java层有着自己的消息循环,因此就不能在Native层进入运行状态,否则的话,就会影响到Android应用程序主进程的正常执行。

       由于当前运行的进程为Render进程,即参数delegate的值等于NULL,因此函数RunNamedProcessTypeMain就会直接调用函数RendererMain使得当前进程进入运行状态,如下所示:

 

[cpp] view plaincopy

  1. int RendererMain(const MainFunctionParams& parameters) {  
  2.   ......  
  3.   
  4.   base::MessageLoop main_message_loop;  
  5.   ......  
  6.    
  7.   {  
  8.     bool run_loop = true;  
  9.     ......  
  10.   
  11.     RenderProcessImpl render_process;  
  12.     new RenderThreadImpl();  
  13.     ......  
  14.   
  15.     if (run_loop) {  
  16.       ......  
  17.       base::MessageLoop::current()->Run();  
  18.       ......  
  19.     }  
  20.   }  
  21.   
  22.   return 0;  
  23. }  

       这个函数定义在文件external/chromium_org/content/renderer/renderer_main.cc。

 

       函数RendererMain首先是创建了一个MessageLoop对象。从前面Chromium多线程模型设计和实现分析一文可以知道,通过默认构造函数创建的MessageLoop对象的消息循环类型为TYPE_DEFAULT,这意味着Render进程的主线程通过类型为MessagePumpDefault的消息泵进入运行状态。

       在Render进程的主线程,也就是当前线程,通过类型为MessagePumpDefault的消息泵进入运行状态之前,会创建一个RenderProcessImpl对象和一个RenderThreadImpl对象。前面分析网页不在单独的Render进程中加载时,我们已经分析过RenderProcessImpl对象和RenderThreadImpl对象的创建过程了。其中,RenderProcessImpl对象的创建过程将会触发在当前进程中启动一个IO线程,用来执行IPC,而RenderThreadImpl对象的创建过程会触发在当前进程中创建一个Client端的IPC通信通道,并且该IPC通信通道是通过从Global Descriptors中获取ID值为kPrimaryIPCChannel的文件描述符创建的,具体可以参考前面分析的ChannelPosix类的成员函数CreatePipe的实现。

       至此,我们就分析完成了Chromium的Render进程的启动过程。对于Chromium的GPU进程和Plugin进程来说,它们的启动过程也是类似的,最主要的区别就是最后执行函数RunNamedProcessTypeMain时,通过不同的入口点函数进入运行状态。因此,后面我们分析GPU进程和Plugin进程的启动过程时,会跳过中间的过程直接进入到对应的运行入口点函数进行分析。例如,对于GPU进程,直接进入到GpuMain函数分析,而对于Pepper Plugin进程来说,直接进入到PpapiPluginMain函数分析。

       回到Chromium的Render进程的启动过程来,总的来说,它主要做的事情就是与Browser进程建立IPC通信通道。这个建立过程如下所示:

       1. Browser进程在启动Render进程之前,会创建一个UNIX Socket,并且使用该UNIX Socket的Server端文件描述符创建一个Server端的IPC通信通道。

       2. Browser进程在启动Render进程之后,会通过Binder IPC将前面创建的UNIX Socket的Client端文件描述符传递给Render进程。

       3. Render进程在进入运行状态之前,会使用前面获得的Client端文件描述符创建一个Client端的IPC通信通道。

       由于Browser进程和Render进程创建的IPC通信通道使用的是同一个UNIX Socket的Server端和Client端文件描述符,因此它们就可以通过该UNIX Socket进行相互通信了。Browser进程和Render进程之间是通过传递IPC消息进行通信的,也就是通过UNIX Socket来传递IPC消息。了解这些IPC消息的传递过程,对阅读Chromium的源代码是非常有帮助的,因为Chromium的源代码到处充斥着IPC消息发送、接收和分发处理逻辑,就如同Android系统里面的Binder IPC一样普遍。因此,在接下来的一篇文章中,我们就将分析Chromium的IPC消息发送、接收和分发过程,敬请关注!更多的信息也可以关注老罗的新浪微博:http://weibo.com/shengyangluo

 

 

https://blog.csdn.net/sauphy/article/details/50507653

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值