Chrome消息系统(2)

http://blog.sina.com.cn/s/blog_5e83fce60100q583.html

前面写了Chrome消息系统(1)

比较抽象,这章来点具体的,当然要结合代码来看了!

现在我要关注的问题是:

一个RenderProcess的消息怎么发送到BrowserProcess进程中的,这个消息走了那些路?注意,所说的IO线程是Browser进程中的IO线程,非Render进程中发的IO所谓的IO线程主要用来接受网络消息和进程间的消息,我先把网络消息抛到一边,单独分析下RenderProcess进程给BrowserProcess的消息是怎样流动的?


通常把网络包分为上行和下行,在这里我把RenderProcessBrowserProcess称为上行消息,BrowserProcessRenderProcess的消息称为下行消息,这章只讨论上行消息。


需要特别注意的几个类分别是:

 SiteInstance: 每个SiteInstance对象对应于一个RenderProcessHost,当创建RenderViewHost时会传递一个SiteInstance实例,该RenderViewHost保存在SiteInstance实例的RenderProcessHost对象中。SiteInstance对应于四种启动方式。RenderProcessHost:每个RenderProcessHost对应于一个RenderProcess进程,RenderProcess进程负责渲染所有的RenderProcessHost中的RenderViewHost的页面。


WebContents: WebContents包括了RenderViewHostNavigationController

RenderViewHostManager:存在于WebContents主要保存一个RenderViewHost对象,但是有一种情况比较复杂,那就是当一个Interstitial webpage存在时,它负责加载Interstitial webpage,并切换到用户请求页面。关于Interstitial webpage请参看上上篇文章的解释。由于这种复杂度,这里没有直接采用RenderViewHost而是采用RenderViewHostManager来控制管理。

NavigationController存在于WebContents中,控制一个TAB的后退,前进,刷新,加载等操作。

RenderViewHost:对应于一个HTML页面

Channel:  用于进程间通讯。

ChannelProxyChannel的代理,并且附加了一些过滤信息

ChannelProxy::Context封装的Channel

SyncChannel:   扩展了ChannelProxy,使之能支持同步信息。

SyncChannel::SyncContextChannelProxy::Context的同步支持的封装。

ResourceDispatcherHost:所有消息将通过ResourceDispatcherHost来过滤,ResourceMessageFilter的消息也会通过ResourceDispatcherHostResourceDispatcherHost还过滤网络消息,并且做相应的转发。

ResourceMessageFilter过滤ChannelProxy的一些相关信息。

MessageLoopForIO 提供支持APC 事件通知的消息循环,这里是支持命名管道的消息。


先大致走一趟上行消息的简单流程,然后结合代码仔细分析。

消息最先达到的地方当然是Channel这个对象了,Channel是被ChannelProxy::Context所封装,所以消息会被分派到ChannelProxy::Context中,ChannelProxy::Context对象是存在于ChannelProxy中的,ChannelProxy::Contex又把消息提交给了ChannelProxyChannelProxy先过滤消息,如果消息没有被过滤掉,把消息推给RenderProcessHost,然后由RenderProcessHost把消息分配给每个相应的RenderViewHost

 上面是上行消息的一个简单的路由过程。其中细节很多,问题也是很多。

0:一个TAB页面是怎么被创建的出来的?

1:ChannelProxy怎么由UI线程给分派到IO线程中去运行?

2:一个命名管道的消息怎么被通知到BrowserProcess进程中的?

3:消息是通过什么方法逐渐往上一层传递的?

4:ResourceMessageFilter是怎么加进ChannelProxy中的,它会过滤掉什么样的消息,什么消息它会继续转发?

5:ResourceMessageFilterResourceDispatcherHost有什么瓜葛?


一:先来分析第一个问题:

一个TAB页面是怎么被创建的出来的?


当用户点击浏览器上的添加按钮或者是从一个网页的一个连接来打开一个新的页面,这样一个新的页面就被创建了。一个WebContents对象对应于一个HTML页面和关联该页面的标签,创建一个页面其实就是创建一个WebContents对象。

上面提到过RenderViewHostManager 存在于WebContents中,RenderViewHostManager 是用来保RenderViewHost对象的,RenderViewHost对象也是在RenderViewHostManager中被创建的。WebContents的构造函数中会传递一个SiteInstance参数, RenderViewHostManager会创建RenderViewHost,然后把创建的RenderViewHost对象关联到SiteInstance相对应的RenderProcessHost中。

仔细看RenderProcessHost的结构会发现,RenderProcessHost中有两个方法:

  void Attach(IPC::Channel::Listener* listener, int routing_id);

  void Release(int listener_id);

用这两个方法来关联和取消关联,具体细节参见前面的UML 类图。

这样一个RenderViewHost的对象就被创建并关联到相应的RenderProcessHost中了。



二:分析第二个问题:

ChannelProxy怎么由UI线程给分派到IO线程中去运行?


根据chrome官方技术文档可以看出来,每个RenderProcessHost对应一个RenderProcess,他们之间通过ChannelProxy来通讯的,也就是说RenderProcessHost是通过ChannelProxyRenderProcessChannelProxy来通讯的。RenderProcessHostRenderProcess分别都拥有一个ChannelProxyRenderProcessHostRenderProcess都是通过他们的大使ChannelProxy来沟通的。先抛开RenderProcess和它的大使ChannelProxy,专心来分析RenderProcessHost和它的大使ChannelProxy

先来分析RenderProcessHost是怎么创建的?

上面分析过,RenderProcessHost是从SiteInstance中获取的,先分析一段SiteInstance获取RenderProcessHost的代码:

RenderProcessHost* SiteInstance::GetProcess()

{

  RenderProcessHost* process = NULL;

  if (process_host_id_ != -1)

// 首先从一个全局的RenderProcessHost对象列表中获取和当前ID相匹配的RenderProcessHost对象

    process = RenderProcessHost::FromID(process_host_id_);

  //如果没有,先恢复,如果恢复不了,那么创建一个行的对象

  if (!process)

{

    //判断是否恢复以前创建的RenderProcessHost对象

    if (RenderProcessHost::ShouldTryToUseExistingProcessHost())

      process = RenderProcessHost::GetExistingProcessHost(browsing_instance_->profile());

    //恢复不了,那么创建一个行的对象

    if (!process)

      process = new RenderProcessHost(browsing_instance_->profile());

// 更新这个RenderProcessHost对象的ID

    process_host_id_ = process->host_id();

    // Make sure the process starts at the right max_page_id

    process->UpdateMaxPageID(max_page_id_);

  }

  DCHECK(process);

  //返回改对象

  return process;

}

这样一个对象就创建了,这里采用的策略是lazycreate,即是到了需要创建的时候才创建该对象。 

注意了,channel并不是在这个时候被创建的,channel是在加载时被创建的,

RenderProcessHost中的Init函数中创建的。

RenderProcess也是在该函数中创建的,关于该函数,很长,我选出其中比较重要的几段,为了不影响阅读,我把一些sandbox的代码给去掉了,简化后的代码如下:

bool RenderProcessHost::Init() {

  // 如果 channel_已经被创建了,那么直接返回,不需要后续处理了。

  if (channel_.get())

    return true;

  // 安装ResourceMessageFilter

  base::Thread* io_thread = g_browser_process->io_thread();

  scoped_refptr<ResourceMessageFilter> resource_message_filter =

      new ResourceMessageFilter(g_browser_process->resource_dispatcher_host(),

                                PluginService::GetInstance(),

                                g_browser_process->print_job_manager(),

                                host_id_,

                                profile_,

                                widget_helper_,

                                profile_->GetSpellChecker());

  CommandLine browser_command_line;

  // 安装IPC channel

  std::wstring channel_id = GenerateRandomChannelID(this);

  channel_.reset(

      new IPC::SyncChannel(channel_id, IPC::Channel::MODE_SERVER, this,

                           resource_message_filter,

                           io_thread->message_loop(), true,

                           g_browser_process->shutdown_event()));

{

一堆生成命令行的代码、被我注释掉了。

}

 //是否是单进程模式

  bool run_in_process = RenderProcessHost::run_renderer_in_process();

  if (run_in_process)

{

    base::Thread *render_thread = new RendererMainThread(channel_id);

    base::Thread::Options options;

    options.message_loop_type = MessageLoop::TYPE_IO;

    render_thread->StartWithOptions(options);

  }

else

 {   HANDLE process;

        if (!process_util::LaunchApp(cmd_line, falsefalse, &process))

          return false;

        process_.set_handle(process);

      watcher_.StartWatching(process_.handle(), this);

    }

  }

  return true;

}

当我开始启动一个浏览器的时候,调试堆栈如下,可以看出Init函数是什么时候第一次被调用的。

 

上面的代码还是运行在UI线程中的,我还没有看出来channel对象是怎么给 分派带IO线程中去运行的?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值