【Chromium】【笔记】《Chromium 如何显示 Web 页面》《chromium多进程架构》

文章目录

    • 原文
    • How Chromium Displays Web Pages
        • 概念
        • The render process
            • RenderWidget和RenderView之间有什么区别?
            • 胶水层 glue
            • Thread in renderer
        • The browser process
            • Low-level browser process objects
            • High-level browser process objects
        • TIPS
            • Life of a "set cursor" message
            • Life of a "mouse click" message
    • Multi-process Architecture
            • render进程的管理
            • 管理 Views
        • 组件和接口
            • render 进程
            • Browser 进程
        • 渲染进程的共享
        • 检测 renderer 的Crash 和 行为
        • renderer 的沙箱运行
        • 不常用内存的归还
        • 插件与扩展

原文

  • How Chromium Displays Web Pages
  • Getting Around the Chromium Source Code Directory Structure
  • Multi-process Architecture
    • 文档部分没更新,新版本的有些已经不同了

How Chromium Displays Web Pages

概念

【Chromium】【笔记】《Chromium 如何显示 Web 页面》《chromium多进程架构》_第1张图片

  • webkit:是Safari、Chroumium和其他基于webkit浏览器的渲染引擎;

    • Port是webkit的一部分,它集成了平台系统依赖服务如:资源装载和图形。
  • glue:把webkit的类型转化为Chromium的类型,这是我们的“webkit嵌入层”;它是Chroumium和test_shell(用来测试webkit的工具)两个浏览器的基础;

  • Renderer / Renderer host:这是Chroumium的“多进程嵌入层”,它代理进程间的通知和命令;

  • webContents:一个重复利用的组件,它是 content 模块中的一个主类,它容易嵌入,可以让多个进程的页面渲染到一个视图中;从Content module可以了解更多的信息。

  • Browser:代表浏览器窗口,它包含多个webContents。

  • Tab Helpers:用来连接webContents的单个对象(通过混入WebContentsUserData),浏览器的webContents持有各种各样的Tab Helper(比如一个favicons,一个infobar等)

The render process

【Chromium】【笔记】《Chromium 如何显示 Web 页面》《chromium多进程架构》_第2张图片

  • 渲染器中最重要的类是 RenderView,位于 /content/renderer/render_view_impl.cc。
    • 这个对象代表一个网页。它处理所有与导航有关的命令,并从浏览器进程中发出。
    • 它派生自RenderWidget,提供绘画和输入事件处理。
    • RenderView通过全局(每个渲染过程)的RenderProcess对象与浏览器进程进行通信。
RenderWidget和RenderView之间有什么区别?
  • RenderWidget通过 实现胶合层中名为WebWidgetDelegate的抽象接口,映射到一个WebCore::Widget对象。这基本上是屏幕上的一个Window,它接收输入事件,我们在其中作画。
    • RenderView继承自RenderWidget,是一个标签或弹出窗口的内容。它除了处理Widget的绘画和输入事件外,还处理导航命令。
  • 只有一种情况下,RenderWidget的存在没有RenderView,那就是网页上的选择框。这些是带有向下箭头的框,会弹出一个选项列表。选择框必须用一个本地窗口来渲染,这样它们就可以出现在其他东西上面,必要时还可以跳出框架。这些窗口需要绘制和接收输入,但并没有一个单独的 “网页”(RenderView)给它们
胶水层 glue
  • 胶水层 glue
    • 将原本不兼容的程序或软件组件结合起来,专门作为两个不兼容的软件片段之间的代理
  • WebKit的 "glue "层:也是一个chromium 和 WebKit 的中间层。
    • WebKit的 "glue "层将Chromium代码库的其他部分与WebCore的数据类型隔离开来,以帮助减少WebCore变化对Chromium代码库的影响
Thread in renderer
  • 每个渲染器都有两个线程
  • 渲染线程是主要对象(如 RenderView 和所有 WebKit 代码)运行的地方。
  • 当它与浏览器进行通信时,消息首先被发送到主线程,然后由主线程将消息分派给浏览器进程。、
  • 这让我们可以从renderer 向browser 同步发送消息。
  • 当JavaScript请求时,获取一个页面的cookie。renderer线程会阻塞,而主线程会排队等待所有收到的消息,直到找到正确的响应。在此期间收到的任何消息随后都会被发布到渲染器线程中进行正常处理。

The browser process

【Chromium】【笔记】《Chromium 如何显示 Web 页面》《chromium多进程架构》_第3张图片

Low-level browser process objects
  • 所有与render 进程的IPC通信都在browser 的I/O线程上完成。这个线程也处理所有的网络通信,这使它不会干扰到用户界面。

  • 当RenderProcessHost在主线程(用户界面运行的地方)上被初始化时,它会创建一个新的渲染器进程和一个ChannelProxy IPC对象,该对象有一个指向渲染器的命名管道。

    • 这个对象在浏览器的I/O线程上运行,监听指向渲染器的指定管道,并自动将所有消息转发回用户界面线程上的RenderProcessHost。
    • 一个ResourceMessageFilter将被安装在这个通道中,它将过滤掉某些可以直接在I/O线程上处理的消息,例如网络请求。这种过滤发生在ResourceMessageFilter::OnMessageReceived。
  • UI线程上的RenderProcessHost负责将所有特定于视图的消息分派给适当的RenderViewHost(它自己处理有限数量的非视图特定的消息)。这种调度发生在RenderProcessHost::OnMessageReceived。

High-level browser process objects
  • 视图特定的消息会进入RenderViewHost::OnMessageReceived。大部分的消息在这里被处理,其余的被转发到RenderWidgetHost基类。

  • 这两个对象映射到renderer 中的RenderView和RenderWidget 。

    • 每个平台都有一个视图类(RenderWidgetHostView[Aura|Gtk|Mac|Win])来 实现与本地视图系统的集成。
  • 在RenderView/Widget 之上是WebContents对象,大多数消息实际上 最终都是对该对象的函数调用。

    • WebContents代表了一个网页的内容。它是内容模块的顶层对象,负责在矩形视图中显示网页。content module
  • WebContents对象包含在一个TabContentsWrapper中。那是在chrome/中,负责一个标签。

TIPS

Life of a “set cursor” message
  • 设置光标是一个典型消息的例子,它从Render发送到Browser,在Render中发生了以下情况:

    • 设置光标消息在Webkit的内部产生,一般是相应输入事件,设置光标消息在RenderWidget::SetCursor(应该是didChangeCursor)开始,这个函数在content/renderer/render_widget.cc中;
    • RenderWidget::SetCursor调用RenderWidget::Send分发该消息,这个函数也被RenderView用来发送消息给Browser,接下来调用RenderThread::Send;
    • 调用IPC::SyncChannel内部代理消息给render的主线程,主线程投递消息给命名管道,从而发送给Browser;
  • 然后Browser采取控制:

    • 在RenderProcessHost中的IPC::ChannelProxy接收Browser的I/O线程的所有消息,首先通过ResourceMessageFilter发送,在I/O线程里直接分发网络和相关消息;既然我们的消息没有被过滤掉,它们继续送到Browser的UI线程(IPC::ChannelProxy在内部处理);
    • 在content/browser/renderer_host/render_process_host_impl.cc的RenderProcessHost::OnMessageReceived收到对应Render进程的所有视图的消息,它直接处理部分消息,其余的转发给发送消息的原RenderView对应的RenderViewHost;
    • 消息到达位于content/browser/renderer_host/render_view_host_impl.cc的RenderViewHost::OnMessageReceived,许多消息在这里处理;但’set cursor’消息不是在这里处理,因为它是从RenderWidget发送,要被RenderWidgetHost处理;
    • 所有未在RenderViewHost处理的消息自动转发给RenderWidgetHost,包括我们的设置光标消息(set cursor message);
    • 在content/browser/render_host/render_view_host_impl.cc的消息映射最后在RenderWidgetost收到该消息,调用适当的界面(UI)函数来设置鼠标光标
Life of a “mouse click” message
  • 发送鼠标点击是一个典型的从浏览器发送消息到渲染进程(renderer)

    • 窗口消息在浏览器的界面线程中的RenderWidgetHostViewWin::OnMouseEvent被接收到,然后调用同类的函数ForwardMouseEventToRender;
    • 该转发函数把输入事件封包成跨平台的WebMouseEvent,最后发送给相关的RenderWidgetHost;
    • RenderWidgetHost::ForwardInputEvent创建一个进程间通信消息ViewMsg_HandleInputEvent,序列化进WebInputEvent,并呼叫RenderWidgetHost::Send;
    • RenderWidgetHost::Send正好转发给RenderProcessHost::Send函数,轮流发送消息给IPC::ChannelProxy;
    • 在内部IPC::ChannelProxy代理消息给浏览器的I/O线程并写进对应的渲染(renderer)进程的命名管道
  • 注意许多其他类型的消息都在WebContents中创建,特别是导航系列,都遵循一个从WebContents到RenderViewHost的简单路径;

  • 然后渲染进程采取控制

  • 渲染主线程侧的IPC::Channel读取浏览器发送过来的消息,IPC::ChannelProxy代理给渲染线程;

  • RendView::OnMessageReceived获取这个消息,许多消息都在这里处理;然而点击消息不在这里处理,它和其他未处理的消息直接到达RenderWidget::OnMessageReceived,再轮流转发给RenderWidget::OnHandleInputEvent;

  • 输入事件转给WebWidgetImpl::HandleInputEvent,在这里转变为一个WebKit platformMouseEvent类,在传入给webkit的内部WebCore::Widget类;

Multi-process Architecture

  • chromium 的多进程,保证了单个render 进程的崩溃不会引起整个浏览器的崩溃、限制了render进程的权限(沙箱)
    • renderer 使用Blink 作为解释和布局HTML 的引擎。

【Chromium】【笔记】《Chromium 如何显示 Web 页面》《chromium多进程架构》_第4张图片

RenderProcess & RenderView与其Host

render进程的管理
  • 每个render进程都有一个全局的RenderProcess对象,它负责管理与父浏览器进程的通信,并维护全局状态。
  • Browser 为每个render 进程维护一个相应的 RenderProcessHost,它管理浏览器状态并和 renderer 通信。
    • Browser和renderer使用 Chromium 的 IPC 系统进行通信。
管理 Views
  • 每个render 进程 都有一个或多个 RenderView 对象,由 RenderProcess 管理,它们对应于content 的标签
    • 相应的RenderProcessHost会维护一个RenderViewHost,与renderer 中的每个view 对应。
      • 每个view都被赋予一个 viewID,用来区分同一renderer 中的多个view。
      • 这些ID在一个renderer 中是唯一的,但在Browser中不是,所以识别一个视图需要一个RenderProcessHost和一个viewID。
    • 从Browser 到特定标签content 的通信是通过这些RenderViewHost对象完成的,它们知道如何通过它们的RenderProcessHost向RenderProcess发送消息,然后再发送到RenderView。
      • RenderViewHost => RenderProcessHost => RenderProcess => RenderView,后两个在 render 进程中。

组件和接口

render 进程
  • RenderProcess处理与Browser 中相应的 RenderProcessHost 的IPC 通信。
    • 每个 render 进程正好有一个 RenderProcess 对象。这就是所有浏览器↔渲染器的通信方式。
  • RenderView对象与Browser 进程中相应的 RenderViewHost(通过RenderProcess)以及我们的WebKit嵌入层进行通信。
    • RenderView 对象在一个标签或弹出窗口中 代表一个网页的内容。
Browser 进程
  • Browser对象代表一个顶级的Browser窗口。
  • RenderProcessHost 对象代表一个 Browser ↔ Renderer IPC连接的Browser 端。
    • 在Browser进程中,每个render 进程都 对应着 一个RenderProcessHost
  • RenderViewHost 对象封装了与远程RenderView 的通信,而RenderWidgetHost处理Browser 中RenderWidget的输入和绘制。

渲染进程的共享

  • 一般来说,每个新的窗口或标签都会在一个新的进程中打开。Browser会生成一个新的进程,并指示它创建一个RenderView

  • 有时,在 tabs 或窗口之间共享 Render 进程是必要的或可取的。

    • 一个web app 打开了一个新的窗口,希望与之进行同步通信,例如,在JavaScript中使用window.open。在这种情况下,当我们创建一个新的窗口或 tab 时,我们需要重新使用该窗口被打开的 进程。
  • 我们也有一些策略,如果进程的总数太大,或者用户已经打开了一个导航到该领域的进程,我们可以将新标签分配给现有的进程。

  • 详情:Process Models

检测 renderer 的Crash 和 行为

  • 与Browser 进程的每个IPC连接都会观察进程句柄。
    • 如果这些句柄被发出信号,说明渲染器进程已经崩溃了,标签会被通知崩溃。
    • 现在,我们通过显示一个 “sad tab” 页面,以通知用户 renderer 已经崩溃了。
      • 该页面可以通过按下重载按钮或开始一个新的导航来重新加载。

renderer 的沙箱运行

  • 鉴于renderer是在一个独立的进程中运行的,我们有机会通过沙盒来限制它对系统资源的访问。

    • 例如,我们可以确保renderer只能通过其父级 Browser 进程来访问网络。同样地,我们也可以利用OS 的内置权限来限制它对文件系统的访问。(进程创建或直接标明文件操作权限)
  • 除了限制renderer 对文件系统和网络的访问外,我们还可以限制它对用户的显示器和相关对象的访问。

    • 我们在一个单独的Windows "桌面 "上运行每个渲染进程,而用户是不可见的。这可以防止被破坏的renderer 打开新的窗口或捕获按键。

不常用内存的归还

  • 鉴于renderer 是在独立的进程中运行的,因此将隐藏的tab 视为低优先级就变得很直接了。

    • 通常情况下,Windows上最小化的进程会将其内存自动放入一个 "available memory "池中
      • 在低内存的情况下,Windows会在换出高优先级的内存之前将这些内存换到磁盘上,以帮助保持用户可见的程序更多的响应。(内存置换)
    • 我们可以将这一原则应用于隐藏标签。当一个渲染进程没有顶层标签时,我们可以释放该进程的 "working set " ,作为对系统的提示,在必要时先将该内存换出到磁盘。
    • 因为我们发现,当用户在两个标签之间切换时,减少 working set 的大小也会降低标签的切换性能,所以我们逐渐释放这部分内存。
      • 这意味着,如果用户切换回最近使用的标签,该标签的内存比最近很少使用的标签更可能 to be paged 。//
      • 拥有足够内存来运行所有程序的用户根本不会注意到这个过程。 Windows只有在需要时才会实际回收这些数据,所以在内存充足的情况下,不会有性能上的影响。
  • 这有助于我们在低内存情况下获得更理想的内存占用率。与很少使用的后台标签相关的内存可以完全被交换出去,而前台标签的数据可以完全被加载到内存中。

  • 相比之下,一个单进程的浏览器会将所有标签的数据随机地分布在其内存中,而且不可能将已使用和未使用的数据分离得如此干净,这既浪费了内存也浪费了性能。(多进程的另一个好处)

插件与扩展

  • 插件和扩展运行在单独的 进程中。(多个tab 的插件同样运行在一个插件进程中)

你可能感兴趣的:(chromium,chromium,多进程架构)