概述
Blink开发并不简单。对于Blink开发新手,不简单是因为,为了实现一个非常快的渲染引擎,已经引入了许多Blink特定的概念和编程约定。
即使对于经验丰富的 Blink 开发人员来说,这也并不容易,因为 Blink 非常庞大,而且对性能、内存和安全性极为敏感。
本文旨在从“3千米”高空视角来描述“Blink如何工作”,希望能够帮助Blink开发者快速熟悉Blink的架构:
- 本文档不是关于Blink细节构成和编码规则(可能变动与过时)的手册、指南。相反,该文档简明地描述了 Blink 的基本原理,这些基础知识在短期内不太可能发生变化,并指出了如果您想了解更多信息可以阅读的资源。
- 该文档不解释特定功能(例如,ServiceWorkers、编辑)。 相反,该文档解释了代码库中广泛(例如,内存管理、V8 API)使用的基本功能
Blink是做什么的?
- Blink是Web Platform的渲染引擎。粗略地说,Blink完成了tab标签中渲染网页内容的所有事情,实现 Web 平台的规范(例如 HTML 标准),包括 DOM、CSS 和 Web IDL。
- 嵌入V8并运行JavaScript
- 从网络栈请求资源
- 构建DOM树
- 计算样式和排版
- 嵌入Chrome合成器并绘制图形
许多“客户”(例如 Chromium、Android WebView 和 Opera)通过public content API嵌入了 Blink。
从代码库的角度看,“Blink”通常指的是//third_party//blink/。从项目的角度来看,“Blink”通常是指实现web平台功能的项目。实现 Web 平台功能的代码主要分布在//third_party/blink/、//content/renderer/、//content/browser/ ,还有一些在其它地方。
进程和线程架构
进程
Chromium是多进程架构。它有一个browser进程和N个沙箱renderer进程。Blink运行在renderer进程。
会创建多少个renderer进程呢?出于安全考虑,隔离跨站点文档之间的内存地址区域(这叫做站点隔离 – Site Isolation)是很重要的。从概念上讲,一个renderer进程应该最多只服务于一个站点。然而,实际情况是,当用户打开大量标签页,或者设备内存不足时,限制一个站点一个renderer进程显得太繁重了。因此,不同站点的标签页或多个iframes可能共享一个renderer进程。这意味着一个标签页中的 iframe 可能由不同的renderer进程托管,而不同选项卡中的 iframe 可能由同一个renderer进程托管。也就是说,renderer 进程、标签页、iframes不存在1对1的关系。
鉴于renderer进程在沙箱中运行,Blink需要通过Browser进程请求系统调用(例如:访问文件,播放音频)和访问用户资料数据(例如:cookie,密码)。Browser进程与renderer进程的进程间通信是通过Mojo实现的(以前是使用Chromium IPC,现在也还有许多地方是使用IPC。然而IPC是被废弃了的,新的代码都应该使用Mojo)。Chromium正在进行Servicification,即把browser进程抽象为一系列的服务。从Blink的角度,Blink可只使用Mojo同services和browser进程进行交互。
想要了解更多,可参考:
- 多进程架构
- Blink mojo编程:platform/mojo/MojoProgrammingInBlink.md
线程
Renderer进程中会创建多少个线程呢?
Blink有一个主线程,N个worker线程,还有几个内部线程。
几乎所有重要的事情都发生在主线程。所有的JavaScript(除了worker),DOM,CSS,样式和排版计算都运行在主线程。Blink高度优化以最大化主线程的性能。
Blink 可能会创建多个worker线程来运行 Web Workers、ServiceWorker 和 Worklets。
Blink 和 V8 可能会创建几个内部线程来处理 webaudio、数据库、GC 等。
通过使用PostTask APIs传递消息来实现跨线程通信。不鼓励共享内存编程,除了出于性能原因确实需要使用它的几个地方。这就是为什么你在 Blink 代码库中看不到很多互斥锁的原因。
更多信息可参考:
- Blink线程编程:platform/wtf/ThreadProgrammingInBlink.md
- Workers:core/workers/README.md
Blink初始化和finalization
Blink 由 BlinkInitializer::Initialize() 初始化。 在执行任何 Blink 代码之前必须调用此方法。
另一方面,Blink没有finalized,即renderer进程没有被清理就强制退出。原因之一是性能。另一个原因是通常很难以优雅有序的方式清理renderer进程中的所有内容(也没有必要这样做)。
目录结构
Content public APIs 和 Blink public APIs
Content public API是embedders能够embed渲染引擎的API层。需要小心维护Content public APIs,因为它是暴露给embedder的。
Blink public APIs是把third_party/blink的功能暴露给Chromium的API层。这个API层源自WebKit。在WebKit时代,Chromium和Safari共享WebKit的实现,因此API层需要把WebKit的能力暴露给Chromium和Safari。现在Chromium是third_party/blink的唯一的embedder,实际上这个API层不是必要的。现在我们正通过将web-platform代码迁移至blink(Onion Soup项目),来积极减少Blink public API的数量。
目录结构和依赖关系
third_party/blink有下面这些目录:
- platform
- Blink较底层的功能,从庞大的core/中分离出来的,例如:几何和图形实用库
- core/ 和 modules
- spec中定义的web平台功能的实现。core/实现DOM关联紧密的功能。module实现比较独立的功能,例如:webaudio,indexeddb
- bindings/core,bindings/modules
- 概念上,bindings/core是core/的一部分,bindings/module是module的一部分,大量使用V8 APIs的文件被放在bindings/{core, modules}中
- controller
- 一组使用/core,/module的上层库。例如devtools 前端
依赖顺序如下:
- Chromium => controller => modules and bindins/modules => core and bindings/core =>
platforms => 低级原语,如//base,//v8,//cc
关于目录结构的更详细信息可参考:blink/renderer/README.md
内存管理
就Blink而言,你需要关心3个内存分配器:
- PartionAlloc
- Olipan(又名Blink GC)
- malloc/free or new/delete
你可以使用USING_FAST_MALLOC在PartionAlloc 堆上分配一个对象:
class SomeObject {
USING_FAST_MALLOC(SomeObject);
static std::unique_ptr<SomeObject> Create() {
return std::make_unique<SomeObject>(); // Allocated on PartitionAlloc's heap.
}
};
应该使用scoped_refptr<> or std::unique_ptr<>来管理由PartionAllock分配的对象的生命周期。强烈建议不要手动管理这类对象的生命周期。Blink中禁止手动调用delete。
可以使用GarbageCollected在Olipan堆上分配一个对象:
class SomeObject : public GarbageCollected<SomeObject> {
static SomeObject* Create() {
return new SomeObject; // Allocated on Oilpan's heap.
}
};
通过Olipan分配的对象的生命周期由GC自动管理。必须使用特殊指针(例如,Member<>、Persistent<>)来保存 Oilpan 堆上的对象。查看Blink GC API reference以了解有关Olipan的编程限制。最重要的限制是,在Oilpan 对象的析构函数中,您不能访问任何其他Oilpan 对象(因为虚构的顺序是不保证的)
如果你既没使用USING_FAST_MALLOC,也没使用GarbageCollected,那么对象就是在系统的malloc堆上分配的。在Blink中是强烈不鼓励这样做的。所有的Blink对象都应该由PartionAlloc或Olipan来分配,规则如下:
- 默认使用Olipan
- 满足如下条件之一使用PartionAlloc
- 1)对象的生命周期非常清晰,std::unique_ptr<> 或 scoped_refptr<> 就足够了
- 2)在 Olipan 上分配对象会带来很多复杂性
- 3)在 Olipan 上分配对象给垃圾收集运行时带来了很多不必要的压力
无论你使用的是PartionAlloc或是Olipan,都需要非常小心,避免创建悬空指针(注意:强烈建议不要使用裸指针)或内存泄露。
如果想要了解更多,可参考:
- 如何使用PartionAlloc
- 如何使用Olipan
- Olipan GC设计
任务调度
为了改善渲染引擎的响应能力,Blink中的任务都应该尽可能的异步执行。应该避免使用同步IPC/Mojo,以及其他可能耗时几毫秒的操作(当然,有一些是无法避免的,像用户的JS执行)。
Renderer进程中的所有tasks都应该以正确的任务类型发布到Blink Schduler中。像这样:
// Post a task to frame's scheduler with a task type of kNetworking
frame->GetTaskRunner(TaskType::kNetworking)->PostTask(..., WTF::Bind(&Function));
Blink Scheduler 维护多个任务队列并巧妙地对任务进行优先级排序,以最大限度地提高用户感知性能。指定适当的任务类型以让 Blink Scheduler 正确和巧妙地安排任务非常重要。
更多细节可参考:如何发布任务
Web IDL bindings
Web IDL
是一种接口描述语言 (IDL) 格式,用于描述旨在在 Web 浏览器中实现的应用程序编程接口 (API)。其采用的动机是希望通过指定JavaScript等语言如何绑定到这些接口,来改善Web编程接口的互操作性。
当JavaScript访问node.firstChild时,node.h中的Node::firstChild被调用。怎么做到的呢?我们来瞧一瞧node.firstChild是个什么样的调用流程:
首先,你需要根据规范定义一个IDL文件:
// node.idl
interface Node : EventTarget {
[...] readonly attribute Node? firstChild;
};
Web IDL语法在WebIDL规范中定义。[…]叫做IDL扩展属性。一些IDL扩展属性在WebIDL规范中定义,其它一些是Blink特有的扩展属性。除了 Blink 特定的 IDL扩展属性,IDL 文件应该以符合规范的方式编写。
第二步,你需要为Node定义一个C++类,并为firstChild实现C++ getter方法:
class EventTarget : public ScriptWrappable { // All classes exposed to JavaScript must inherit from ScriptWrappable.
...;
};
class Node : public EventTarget {
DEFINE_WRAPPERTYPEINFO(); // All classes that have IDL files must have this macro.
Node* firstChild() const { return first_child_; }
};
在一般情况下,就是这样。 当您构建 node.idl 时,IDL 编译器会自动为 Node 接口和 Node.firstChild 生成 Blink-V8 绑定。自动生成的bindings位于
//src/out/{Debug,Release}/gen/third_party/ blink/renderer/bindings/core/v8/v8_node.h。当 JavaScript 调用 node.firstChild 时,V8 调用v8_node.h 中的 V8Node::firstChildAttributeGetterCallback(),然后调用C++中定义的的 Node::firstChild()。
想要了解更多,可参考:
- 如何添加Web IDL绑定:bindings/IDLCompiler.md
- 如何使用IDL扩展属性:bindings/IDLExtendedAttributes.md
- WebIDL规范
Web IDL 编译器
IDL compiler或bindings生成器将Web IDL转换成C++代码,实现V8 与 Blink之间的绑定。也就是说,当从 JavaScript 使用 Web IDL 接口中的属性或方法时,V8 调用绑定代码,该代码调用 Blink 代码。
渲染流水线
从HTML文件发送到Blink到内容被渲染到屏幕上,是个很长的过程。渲染流水线架构如下:
今天的文章blink原理_blink是啥「建议收藏」分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/65713.html