Open anjia opened 6 years ago
“Rust 是专门针对 Servo 的需求而设计的一种新的编程语言”,这个结论有出处吗? “从而保证了内存安全和数据竞争自由”——“数据竞争自由”,是不会出现“竞争读取数据”这种情况的意思?
@cncuckoo 有。https://github.com/servo/servo/wiki/Design 的第二段
Because C++ is poorly suited to preventing these problems, Servo is written in Rust, a new language designed specifically with Servo's requirements in mind. Rust provides a task-parallel infrastructure and a strong type system that enforces memory safety and data race freedom.
“从而保证了内存安全和数据竞争自由”——“数据竞争自由”,是不会出现“竞争读取数据”这种情况的意思?
确切地说,是不会出现多个线程同时不安全“写”的情况(当都同时“读”的时候,不构成竞争)。因为 Rust 可以让多个线程安全的并行,至于如何避免数据竞争,我还没深究 :) 。这里有篇文章 https://hacks.mozilla.org/2017/06/avoiding-race-conditions-in-sharedarraybuffers-with-atomics/#data-race
PS. 我把“数据竞争自由” 改成 “避免数据竞争”,感觉更好点~!
Because C++ is poorly suited to preventing these problems, Servo is written in Rust, a new language designed specifically with Servo's requirements in mind.
这句话如果理解成:“Rust 是专门针对 Servo 的需求而设计的一种新的编程语言”,有点片面了。
我觉得理解成:“Rust在设计时充分考虑了Servo的需求”,可能更符合Rust作为一门通用系统语言的定位。
“Servo 是一款现代化的高性能浏览器引擎,专为应用程序和嵌入式应用而设计。”
还有这句对官网的slogan的翻译,容易让人误解。后半句似乎翻译成:“既支持常规应用,也支持嵌入使用”更好。
@cncuckoo 李老师,已修正。更新后,意思确实更精准了,手动🌹
更多相关,详见 label:servo
“Rust 是专门针对 Servo 的需求而设计的一种新的编程语言” 这句话太片面了
“Rust 是专门针对 Servo 的需求而设计的一种新的编程语言” 这句话太片面了
@vle-william 嗯,这个是站在“起源”的角度说的。发展到现在,现在再这么说,是片面的。
并发是拆分任务以便交叉执行;并行是同时执行多个任务以提高速度??
并发是拆分任务以便交叉执行;并行是同时执行多个任务以提高速度??
@zzz6519003 嗯,是的。图解的话,大约如下:上-并发;下-并行。
how sweet~
Servo 的设计架构
对,我就是 Servo 的官方 Logo
Servo 是一款现代化的高性能浏览器引擎,既支持常规应用,也支持嵌入使用。官网 https://servo.org
她由 Mozilla 开发,由三星集团移植到 Android 系统和 ARM 处理器,旨在创造一个大规模并行计算的环境,Servo 也与 Rust 编程语言有着共生的关系。源自 https://zh.wikipedia.org/wiki/Servo
1. 设计简介
Servo 是一个新的 Web 浏览器引擎。她的目标是创建一个多层级的高并发架构,同时在架构层面消除与错误的内存管理、数据竞争相关的常见 bug 和安全漏洞。
因为 C++ 不适合处理这类问题,所以 Servo 是用 Rust 语言编写的。Rust 在设计的时候充分考虑了 Servo 的需求,它提供了任务并行的基础架构和强类型系统,从而保证了内存安全、避免了数据竞争。
在设计的时候,Servo 的架构师们会优先考虑现代 Web 平台的以下特性:高性能、动态、富媒体应用,可能会牺牲一些无法优化的特性。他们想知道一个快速响应的 Web 平台是什么样子的,然后再实现它。
Servo 专注于实现一个功能完备的 Web 浏览器引擎和可靠的嵌入式引擎,前者(Web 浏览器引擎)使用了基于 HTML 的用户界面 Browser.html。尽管 Servo 最初只是一个研究型项目,但在开发它的时候就以提供可用于生产环境的代码为目标。目前,Servo 的一些组件已经迁移到了 Firefox 浏览器。
并行并发的策略
并发是拆分任务以便交叉执行;并行是同时执行多个任务以提高速度。Servo 在以下环节中用到了并行和并发。
显示列表
分成子树,并行渲染子树,并将其内容保留在 GPU 上。挑战
2. 任务架构
图1. 任务监管图,源自 servo/wiki/Design
图2. 任务通信图,源自 servo/wiki/Design
说明
我们可以把每个 constellation(见“附录.术语”小节)实例看做是浏览器的单个页签或者窗口,它管理着接收输入的任务管道,针对 DOM 运行 JavaScript,执行布局,构建
显示列表
,将显示列表
渲染到瓦片上,最后把最终图像合成到屏幕上。这个管道由四个主要任务组成:
显示列表
,显示列表
会被发送到渲染任务。显示列表
,并将可见部分渲染到一个或多个瓦片上,尽可能并行。管道中的多任务通信涉及到两种复杂的数据结构:DOM 和
显示列表
。DOM 从内容传到布局,显示列表
从布局传到渲染。找出一种有效且类型安全的方式来表示、共享和传递这两种数据结构是该项目的诸多挑战之一。写时复制 DOM
Servo 的 DOM 树节点是有版本控制的,它们可以在单个 writer 和多个 reader 之间共享。DOM 使用写时复制(copy-on-write)的策略允许当有多个 reader 时 writer 也能修改 DOM。writer 总是内容任务,reader 总是布局任务或其子任务。
DOM 节点是 Rust 值(Rust value),而 Rust 值的生命周期由 JavaScript 垃圾收集器管理。JavaScript 直接访问 DOM 节点,而没有依赖 XPCOM 或其它类似的基础设施。
DOM 接口目前不是类型安全的,这可能会导致不正确的节点管理。消除这类不安全是该项目的一个必要的高优先级目标;由于 DOM 节点具有复杂的生命周期,这将会带来一些挑战。
显示列表
Servo 的渲染完全由
显示列表
驱动,显示列表
是由布局任务创建的一系列高级绘图命令。Servo 的显示列表
是完全不可变的,因此它可以被同时运行的多个渲染任务所共享。这与 Webkit 和 Gecko 的渲染器不同:WebKit 的渲染器没有使用显示列表
;Gecko 的渲染器使用了显示列表
,但它在渲染期间还会查询额外的信息。3. JavaScript 和 DOM 绑定
目前,Servo 使用的脚本引擎是 SpiderMonkey(可插拔引擎是一个长期的、低优先级的目标)。每个内容任务都有自己的 JavaScript 运行时。DOM 绑定使用原生的 JavaScript 引擎 API 而不是 XPCOM。从 WebIDL 自动生成绑定是一个高优任务。
4. 多进程架构
与 Chromium 和 WebKit2 类似,Servo 的架构师们打算做一个可信任的应用程序进程和多个不太可信的引擎进程。高级 API 实际上是基于 IPC 的,非 IPC 实现可能用于测试和单进程用例(虽然预计最糟糕的时候也会用于多进程)。引擎进程将使用操作系统沙箱工具来限制对系统资源的访问。
目前,Servo 并不打算像 Chromium 那样采用极端沙箱(extreme sandboxing),主要是因为锁定沙箱会导致大量的开发工作(特别是在 Windows XP 和旧版 Linux 等低优先级的平台上),并且该项目的其它方面的优先级更高一点。Rust 的类型系统还为内存安全漏洞增加了一层重要的防御功能,虽然仅凭这一点并不能使沙箱在防御不安全代码、类型系统中的错误以及第三方/主机库等方面变得不那么紧迫,但相对于其他浏览器引擎它确实能显著减少 Servo 的攻击面。此外,Servo 的架构师们对某些沙箱技术有性能方面的顾虑(例如,将所有 OpenGL 调用代理到单独的进程)。
5. I/O和资源管理
网页依赖于各种各样的外部资源,而这些资源具有很多的检索和解码机制。这些资源会被缓存在多处,比如磁盘、内存。在并行浏览器的设置中,这些资源一定会在并发的多个 worker 之间调度。
通常,浏览器是单线程的,会在“主线程”上执行 I/O,而“主线程”同时又担负着大部分的计算任务,这就会导致延迟问题。而 Servo 中没有“主线程”,所有外部资源的加载都由一个资源管理任务来处理。
浏览器有很多缓存,而 Servo 的基于任务的架构意味着它可能会拥有比现有浏览器引擎还多的缓存(例如,我们在拥有全局任务缓存的同时,也拥有着一个本地任务缓存,它存储着来自全局缓存的结果,以通过调度程序来保存往返记录)。Servo 应该有一个统一的缓存机制,以便在低内存的环境中也运行良好。
附录. 术语
constellation
:该线程控制相关网页内容。在支持多页签的浏览器中,可以把它当做单个页签的拥有者;它封装了会话历史记录,知道 frame 树中的所有 frame,是每个 frame 管道的拥有者。管道(pipeline)
:为特定文档封装了脚本线程、布局线程和渲染线程之间的通信。每个管道都有一个全局唯一的 id,可以从 constellation 里访问到它。脚本线程/脚本任务(script thread/script task)
:这个线程执行 JavaScript,并存储同源下所有文档的 DOM 表示。它可以把从 constellation 接收到的输入事件转换为规范里定义的 DOM 事件,也可以在收到新页面的时候调用 HTML 解析,也可以为事件评估 JS。布局线程(layout thread)
:这个线程负责将 DOM 树布局到特定文档的层(layer)上。它会收到来自脚本线程的命令,要么是为渲染线程生成一个新的显示列表
,要么是为脚本线程返回页面的布局结果。显示列表(display list)
:一个具体的渲染说明(高级绘图命令)列表。显示列表
是发生在布局之后的,因此所有的项都有相对堆叠上下文的像素位置,并且已经应用了 z-index,所以后加入显示列表
的项将始终在其它项的上面。渲染线程/绘制线程(renderer thread/paint thread)
:这个线程负责将显示列表
转换成一系列的绘图命令。该绘图命令会将关联文档的内容渲染在一个缓冲区里,之后会被发送到合成器。合成/合成器(Compositor)
:负责 Web 内容的合成渲染,并将它们尽可能快地显示在屏幕上。也负责从操作系统接收输入事件,并将它们转发到 constellation 线程。小结
本文主要介绍了 Servo 的设计概况,重点介绍了它基于任务的整体架构及其四个主要任务(也称“线程”,在 Servo 的这个上下文里),即脚本任务、布局任务、渲染任务、合成任务。下图便是对上述内容的一个总结,希望对大家有所帮助和启发。
图3. Servo 概况
下一步
后续,我会继续探索更多详细内容,敬请期待。关于 Quantum 和 Servo,如果您有其它更想知道的,欢迎留言。
致谢
感谢 @cncuckoo(李松峰老师)和 @liuguanyu(二哥)对本文提出的指导意见和建议。手动送花花。🌹
参考