How Blink works

How Blink works

原文链接:https://docs.google.com/document/d/1aitSOucL0VHZa9Z2vbRJSyAIsAz24kX8LFByQ5xQnUg/edit

     Blink是Web平台的渲染引擎。 简单地说,Blink实现了所有在浏览器选项卡中呈现的内容:

  • 实施网络平台的规范(例如HTML标准),包括DOM,CSS和Web IDL
  • 嵌入V8并运行JavaScript
  • 从基础网络堆栈请求资源建立DOM树
  • 计算样式和布局
  • 嵌入Chrome合成器并绘制图形

    许多客户(例如Chromium,Android WebView和Opera)通过内容公开API嵌入了Blink。

 

      从代码库的角度来看,“Blink”通常是指代码/ third_party / blink /。 从项目的角度来看,“Blink”通常表示实现Web平台功能的项目。 实现Web平台功能的代码跨越/ third_party / blink /,/content / renderer /,/content /浏览器/和其他地方。

进程/线程架构

进程

        Chromium是一个多进程架构。 Chromium具有一个browser进程和N个sandboxed renderer 进程。Blink在renderer进程中运行。

        创建了多少个renderer进程?出于安全原因,隔离跨站点文档之间的内存地址区域很重要(这称为站点隔离)。从概念上讲,每个renderer进程最多应专用于一个站点。但是实际上,当用户打开太多选项卡或设备没有足够的RAM时,有时将每个renderer进程限制在一个站点上有时会很繁琐。然后,renderer进程可以由从不同站点加载的多个iframe或标签共享。这意味着一个选项卡中的iframe可以由不同的renderer进程托管,而不同选项卡中的iframe可以由相同的renderer进程托管。渲染器进程,iframe和制表符之间没有1:1映射。

        假设renderer进程在沙箱中运行,Blink需要让浏览器进程调度系统调用(例如,文件访问,播放音频)并访问用户个人资料数据(例如Cookie,密码)。这种browser-render进程的通信是由Mojo实现的。 (注意:过去我们使用的是Chromium IPC,但仍然有很多地方在使用它。但是,它已被弃用,并在后台使用Mojo。)在Chromium方面,服务化正在进行中,并将browser进程抽象为一组“服务。从Blink角度来看,Blink可以仅使用Mojo与服务和browser进程 进行交互。

                                

 

线程

        在renderer进程中创建了多少个线程?

        Blink有一个主线程,N个工作线程和几个内部线程。

        几乎所有重要的事情都在主线程上发生。 所有JavaScript(except workers),DOM,CSS,样式和布局计算都在主线程上运行。 假设主要是单线程体系结构,Blink进行了高度优化,以最大程度地发挥主线程的性能。

        Blink可能会创建多个工作线程来运行Web Worker,ServiceWorker和Worklets。

        Blink和V8可能会创建几个内部线程来处理webaudio,数据库,GC等。对于跨线程通信,必须使用通过PostTask API传递消息。 除了出于性能原因确实需要使用它的几个地方,不建议使用共享内存编程。 这就是为什么在Blink代码库中看不到很多MutexLocks的原因。

                                   

 

Blink的初始化和完成

       Blink由BlinkInitializer :: Initialize()初始化。 在执行任何Blink代码之前,必须调用此方法。

另一方面,Blink永远不会完成。 即,renderer 进程被强制退出而不进行清理。 原因之一是性能,另一个原因是,通常很难以一种有序的方式清理renderer 进程中的所有内容(这是不值得的工作)。

目录结构

Content 公共API和Blink公共API

Content 公共API是使嵌入程序嵌入呈现引擎的API层。 Content 公共API必须小心维护,因为它们会暴露在嵌入程序中。

Blink公共API是将/third_party / blink /的功能公开给Chromium的API层。 该API层只是从WebKit继承的历史工件。 在WebKit时代,Chromium和Safari共享了WebKit的实现,因此需要API层才能将WebKit的功能公开给Chromium和Safari。 既然Chromium是/third_party / blink /的唯一嵌入者,那么API层就没有意义了。 通过将网络平台代码从Chromium移至Blink(该项目称为Onion Soup),我们正在积极减少Blink公共API的数量。

                                      

 

目录结构和依赖性

/ third_party / blink /具有以下目录。

  • platform/

        Blink的较低级功能的集合,这些功能是从整体内核中剔除的。 例如,几何和图形工具。

  • core/ and modules/

        规范中定义的所有Web平台功能的实现。 core /实现与DOM紧密结合的功能。 modules/实现更多独立功能。 例如 网络音频,indexeddb。

  • bindings/core/ and bindings/modules/

        从概念上讲,bindings / core /是core /的一部分,而bindings / modules /是modules /的一部分。 大量使用V8 API的文件被放在bindings / {core,modules}中。

  • controller/

        controller/一组使用core /和modules /的高级库。 例如devtools前端。

 

依存关系按以下顺序流动:

       Chromium => controller/ => modules/ and bindings/modules/ => core/ and bindings/core/ => platform/ => low-level primitives such as //base, //v8 and //cc

       Blink仔细维护暴露于// third_party / blink /的低级基元列表。

WTF

      WTF是“Blink-specific base”库,位于platform / wtf /。 我们正在尝试尽可能多地统一Chromium和Blink之间的编码原语,因此WTF应该很小。 需要该库是因为确实需要针对Blink的工作量和Oilpan(Blink GC)优化许多类型,容器和宏。 如果在WTF中定义了类型,则Blink必须使用WTF类型,而不是// base或std库中定义的类型。 最受欢迎的是vectors, hashsets, hashmaps and strings。 Blink应该使用WTF :: Vector,WTF :: HashSet,WTF :: HashMap,WTF :: String和WTF :: AtomicString而不是std :: vector,std :: * set,std :: * map和std :: string 。

内存管理

        就Blink而言,您需要关心三个内存分配器:

  • PartitionAlloc
  • Oilpan (a.k.a. Blink GC)
  • malloc/free or new/delete (banned)

 

       您可以使用USING_FAST_MALLOC()在PartitionAlloc的堆上分配一个对象:

 

       由PartitionAlloc分配的对象的生存期应由scoped_refptr <>或std :: unique_ptr <>管理。 强烈建议不要手动管理生命周期。 Blink禁止手动删除。

      您可以使用GarbageCollected在Oilpan的堆上分配对象。

 

        Oilpan分配的对象的生存期由垃圾收集自动管理。您必须使用特殊的指针(例如Member &lt;&gt;,Persistent &lt;&gt;)将对象保存在Oilpan的堆上。最重要的限制是不允许您在Oilpan对象的析构函数中操作任何其他Oilpan对象(因为无法保证销毁顺序)。

如果您既不使用USING_FAST_MALLOC()也不使用GarbageCollected,则在系统malloc的堆上分配对象。Blink不建议这样做。所有Blink对象应由PartitionAlloc或Oilpan分配,如下所示:

        默认情况下使用Oilpan。仅在以下情况下才使用PartitionAlloc:

  • 1)对象的生存期非常清楚并且std :: unique_ptr &lt;&gt;或scoped_refptr &lt;&gt;足够,
  • 2)在Oilpan上分配对象会带来很多复杂性,或者
  • 3)在Oilpan上分配对象会带来很多复杂性给垃圾收集运行时带来了不必要的压力。

       无论使用PartitionAlloc还是Oilpan,都必须非常小心,不要创建悬空的指针(注意:强烈建议不要使用原始指针)或内存泄漏

任务调度

        为了提高渲染引擎的响应能力,Blink中的任务应尽可能异步执行。 不鼓励同步IPC / Mojo和任何其他可能花费几毫秒的操作(尽管有些操作是不可避免的,例如用户的JavaScript执行)。

renderer进程中的所有任务都应使用正确的任务类型发布到Blink Scheduler,如下所示:

 

      Blink Scheduler维护多个任务队列,并巧妙地对任务进行优先级排序,以最大化用户感知的性能。 重要的是要指定适当的任务类型,以使Blink Scheduler正确,智能地调度任务。

Page, Frame, Document, DOMWindow etc

概念

        Page, Frame, Document, ExecutionContext and DOMWindow are the following concepts:是以下概念:

  • Page对应于选项卡的概念(如果未启用下面说明的OOPIF)。 每个renderer 进程可能包含多个选项卡。
  • Frame对应于框架(主框架或iframe)的概念。 每个页面可以包含一个或多个以树状层次结构排列的框架。
  • DOMWindow对应于JavaScript中的窗口对象。 每个框架都有一个DOMWindow。
  • Document对应于JavaScript中的window.document对象。 每个框架都有一个文档。
  • ExecutionContext是抽象一个Document(用于主线程)和WorkerGlobalScope(用于一个工作线程)的概念。

Renderer process : Page = 1 : N.

Page : Frame = 1 : M.

Frame : DOMWindow : Document (or ExecutionContext) = 1 : 1 : 1在任何时间点,但映射可能随时间而变化。 例如,考虑以下代码:iframe.contentWindow.location.href =“ https://example.com”;

在这种情况下,将为https://example.com创建一个新的DOMWindow和一个新的Document。 但是,可以重复使用框架。(注意:确切地说,在某些情况下会创建一个新的Document,但是DOMWindow和Frame会被重用。甚至还有一些更复杂的情况。)

 

V8 and Blink

Isolate, Context, World

当您编写涉及V8 API的代码时,理解Isolate, Context, World的概念很重要。它们分别在代码库中由v8 :: Isolate,v8 :: Context和DOMWrapperWorld表示。

Isolate对应于物理线程。Isolate:Blink中的物理线程= 1:1。主线程具有自己的隔离。辅助线程具有其自己的隔离。

Context对应于全局对象(在使用Frame的情况下,它是Frame的窗口对象)。由于每个Frame都有其自己的窗口对象,因此render进程中存在多个Context。调用V8 API时,必须确保您使用的是正确的Context。否则,v8 :: Isolate :: GetCurrentContext()将返回错误的上下文,在最坏的情况下,它将最终导致对象泄漏并导致安全问题。

World 是支持Chrome扩展程序的内容脚本的概念。World 与Web标准中的任何内容都不对应。内容脚本希望与网页共享DOM,但是出于安全原因,必须将内容脚本的JavaScript对象与网页的JavaScript堆隔离。 (还必须将一个内容脚本的JavaScript堆与另一个内容脚本的JavaScript堆隔离。)为了实现隔离,主线程为网页创建了一个主World ,为每个内容脚本创建了一个隔离World 。主世界和孤立世界可以访问相同的C ++ DOM对象,但是它们的JavaScript对象是孤立的。通过为一个C ++ DOM对象创建多个V8包装器来实现这种隔离。即每个World 一个V8包装器。

                                              

 

Context, World and Frame之间有什么关系?

想象一下,主线程上有N个World (一个主World +(N-1)个孤立World )。 然后,一个框架应具有N个窗口对象,每个窗口对象用于一个World 。 Context是与窗口对象相对应的概念。 这意味着,当我们有M个框架和N个World 时,我们有M * N个上下文(但是这些上下文是惰性创建的)。

如果是工人,则只有一个World 和一个全局对象。 因此,只有一个上下文。

同样,当您使用V8 API时,请务必谨慎使用正确的上下文。 否则,您最终将在孤立的World 之间泄漏JavaScript对象并造成安全灾难(例如,来自A.com的扩展程序可以操纵来自B.com的扩展程序)。

V8 API

/v8/include/v8.h中定义了许多V8 API。 由于V8 API是低级的并且难以正确使用,因此platform / bindings /提供了一堆包装V8 API的帮助程序类。 您应该考虑尽可能使用助手类。 如果您的代码必须大量使用V8 API,则应将文件放在bindings / {core,modules}中。

V8使用句柄指向V8对象。 最常见的句柄是v8 :: Local <>,它用于从计算机堆栈指向V8对象。 在计算机堆栈上分配v8 :: HandleScope之后,必须使用v8 :: Local <>。 v8 :: Local <>不应在机器堆栈外部使用:

 

如果要从计算机堆栈外部指向V8对象,则需要使用包装器跟踪。 但是,您必须非常小心,不要用它创建引用 循环。 通常,V8 API很难使用。 

V8 wrappers

V8包装器每个C ++ DOM对象(例如Node)都有其对应的V8包装器。 确切地说,每个C ++ DOM对象每个world都有其对应的V8包装器。

V8包装器对其相应的C ++ DOM对象有强引用。 但是,C ++ DOM对象仅对V8包装程序具有弱引用。 因此,如果您想让V8包装器存活一段时间,则必须明确地做到这一点。 否则,将过早回收V8包装器,并且V8包装器上的JS属性将丢失...

如果我们什么都不做,GC会收集child,因此child.foo会丢失。 为了使div.firstChild的V8包装器保持活动状态,我们必须添加一种机制,“只要div所属的DOM树可以从V8到达,则使div.firstChild的V8包装器保持活动状态”。

Rendering pipeline

从将HTML文件传送到Blink到在屏幕上显示像素还有很长的一段路要走。 渲染管道的架构如下

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值