2.基于 Blink 的 Chrominum 浏览器结构
2.1 Chrominum 浏览器的架构及模块
Chromium也是基于WebKit(Blink)开发的,并且在WebKit的移植部分中,Chromium也做了很多有趣的事,所以通过Chromium可以了解如何基于WebKit构建浏览器。,更进一步说,chromium也做了很多技术创新,将很多先进的理念引入到浏览器领域。
(1)从架构上看,WebKit(Blink)只是其中的一块,,还包括GPU/CommandBuffer(硬件架构)、V8 JavaScript引擎、沙箱模型、CC(Chromium Compositor)、IPC、UI等。
- GPU/Command Buffer ,硬件加速架构
- Content 模块、Content 接口(API):Chromium 对渲染网页功能的抽象,将下面的渲染、安全、插件机制隐藏起来,提供一个接口层(供上层模块或其他项目使用)。也可以在 WebKit 的 Chromium 移植上渲染网页内容(无法获取 Content 层中实现的沙箱模型、跨进程的 GPU 硬件加速机制等)
- Content Shell:使用 Content API 来包装的一层简单的壳,用户可以使用 Content 模块来渲染和显示网页内容。作用可以用来测试 Content 模块很多功能的正确性,也可以作为一种外部项目开发的参考
- Android WebView:利用 Chromium 实现来替换原来 Android 默认的 WebView
“Content模块”和“Content API(接口)”,是对渲染网页功能的抽象,“Content”的本意是指网页的内容,这里是指用来渲染网页内容的模块。这个渲染与WebKIt(Blink)的渲染功能的区别是,后者没有办法获得沙箱模型、跨进程的GPU硬件加速机制、众多的HTML5功能。因为这些都是在 Content 层里实现的。
“Content模块”和“Content API”将下面的渲染机制、安全机制和插件机制等隐藏起来,提供一个接口层。该接口层被内部的 Chrominum 浏览器、Content Shell等调用,外部被CEF(Chromium Embedded Framework)、OPera浏览器调用。
Chrominum 浏览器和 Content Shell 是构建在 Content API之上的两个浏览器。 Chrominum 浏览器是具有完整功能的浏览器;而Content Shell是一个简单的“壳”的浏览器,使用content模块来渲染和显示网页内容,其作用是测试content模块很多如渲染、硬件加速等功能的正确性,另外是一个参考,可以被很多外部项目参考来开发基于“Content API”的浏览器或者各种类型的项目。
在Andriod系统上,Content Shell的作用更大,因为chromium浏览器的部分代码并没有开源,只能依赖Content Shell。最右边的 Andriod WebView,是为了满足Android系统上的WebView而设计的,其思想是利用Chromium的实现来替换原来的Andriod系统默认的WebView。
(2)多进程模型
在WebKIt内核之上,Chromium率先引进多进程模型。多进程不可避免带来一些问题和复杂性,但可以带来以下三点好处:
- 避免因单个页面的不响应或者崩溃而影响整个浏览器的稳定性,特别是对用户界面的影响。
- 当第三方插件崩溃时,不会影响页面或者浏览器的稳定性,这时因为第三方插件也被使用单独的进程来运行。
- 方便安全模型的实施,也就是说沙箱模型是基于多进程架构的。
图中给出了Chromium浏览器器常用的多进程模型,因为其架构的灵活性,可以为使用者提供通过简单的设置来随意改变进程的模型方式,图中方框代表进程,连线代表IPC进程间的通信,没有连线的表明不同类型进程之间没有通信,如NPAPI插件和GPU之间就没有通信,这是因为NPAPI是早期的插件标准,它没有定义使用GPU加速的接口。
- Browser 进程:浏览器主进程,负责浏览器界面的显示、各个页面的管理,是所有其他类型进程的祖先,负责它们的创建和销毁等工作,有且仅有一个。
- Renderer 进程:网页的渲染进程,负责页面的渲染工作,Blink/WebKit 的渲染工作主要在这个进程中完成,Renderer 进程数量不一定与用户打开页面的数量一致:且在沙箱模型启动的情况下,该进程可能会发生一些变化。
- NPAPI 插件进程:该进程是为 NPAPI 类型的插件而创建,原则是每种类型的插件只会创建一次,而且仅当使用时才被创建,有多个网页需要使用同一类型的插件的时候,插件进程会为每个使用者创建一个实例,即插件进程是被共享的。
- GPU 进程:最多只有一个,当且仅当 GPU 硬件加速打开的时候才会被创建,主要用于对 3D 图形加速调用实现。
- Pepper 插件进程: 同 NPAPI 插件进程,为 Pepper 插件而创建。
- 其他类型的进程:如 Linux 下的 Zygote 进程(创建 Renderer 进程)、Sandbox 准备进程(安全机制)
Chrominum 多进程模型特征:
- Browser 进程和页面的渲染时分开的,页面渲染进程的崩溃不会导致浏览器主界面的崩溃
- 每个网页是独立的进程,这保证了页面之间相互不影响
- 插件进程也是独立的,插件本身的问题不会影响到网页和主界面
- GPU 硬件加速进程也是独立的,且只有一个 GPU 进程
而在 Android 平台上,目前版本不支持插件,所以没有插件今年成,GPU 进程演变成 Borwser 进程的一个GPU线程,可以节省资源;Renderer进程会演变成服务(Service)进程。由于 Android 系统的局限性,Rneder 进程的数目会被严格限制,所以移动端浏览器引入了“影子”标签的概念,移动端浏览器会将后台的网页所使用的渲染设施都清除,当用户再次切换回来的时候,网页需要重新加载和渲染
- process-per-site-instance:每一个页面都创建一个独立的 Renderer 进程,不管这些页面是否属于同一个域。这样好处是每个页面互相不影响,但是会造成资源的浪费。
- process-per-site:属于同一个域的页面共享同一个进程,不属于同一个域的页面分属不同的进程,这样的好处是进程可以共享,内存消耗相对小,但是副作用是导致Renderer进程特别大。
- process-per-tab:chromium的默认行为,无论是不是不同域不同实例,每个标签页都创建一个独立进程(Chromium 默认行为),会造成资源浪费。
- single process:不为页面创建任何独立的进程,所有渲染工作都在 Browser 进程中的多个线程中进行,这个属于实验性质且稳定性差,只有在比较单进程和多进程对比时才使用。在Andriod WebView中,该模式被采用。
(3)Browser 进程和 Renderer进程
Browser进程和Renderer进程都是在WebKit的接口之外由Chromium引入的,其如何利用WebKit渲染网页见下图所示:
最下面的时WebKit接口层,一般是基于Webkit接口层的浏览器直接在上面构建,而没有引入复杂的多进程机构。
Webkit 黏附层的出现主要是因为 Chrominum 中的一些类型和 Webkit 内部不一致,所以需要一个简单的桥阶层
Render 进程主要处理进程间通信,接受来自 Browser 进程的请求,并调用相应的 Webkit 接口层。同时,将 Webkit 的处理结果发送回去
在 Browser 进程中,与 Render 进程相应的就是 RenderHost,其目的是处理同 Render 进程之间的通信,用来给 Render 进程发送请求并接收来自 Render 进程的结果
Web Contents表示是就是网页的内容,网页由多个需要绘制的内容,同时包括显示网页内容的一个子窗口(在桌面系统上),这个子窗口最后被嵌入浏览器的用户界面,作为i它的一个标签页。
(4)多线程模型
每个进程内部都有很多线程,对于 Browser 进程,多线程的目的主要是为了保持用户界面的高度响应,保证 UI(Browser 主线程)线程不会被其他费时的操作阻碍从而影响对用户操作的响应。而在 Render 进程中,Chrominum 则不让其他操作阻止渲染线程的快速运行,为了利用多核的优势,Chromium将渲染过程管线化,这样可以让渲染的不同阶段在不同线程执行。
网页渲染的过程在进程模型中的工作方式如下:
- Browser 进程收到用户的请求,首先有 UI 线程处理,而且将相应的任务给 IO 线程,IO 线程随即将该任务传递给 Render 进程
- Render 进程的 IO 线程经过简单的解释后交给渲染线程。渲染线程接受请求,加载网页并渲染网页,这其中可能需要 Browser 进程获取资源和需要 GPU 进程来帮助渲染。最后 Render 进程将结果由 IO 线程传递给 Browser 进程
- 最后,Browser 进程接收到结果并将结果绘制出来
(5) Content 接口
Content 接口提供了一层对多进程进行渲染的抽象接口,其目标是要支持所有的 HTML5 功能、GPU 硬件加速功能和沙箱机制,这可以让 Content 接口的使用者们不需要很多的工作即可得到强大的能力
Content 接口的相关代码按照功能分成六个部分,每个部分的接口一般可分成两类,第一类是调用者(Chrominum 浏览器、CEF3和Content Shell等)调用的接口,另一类是调用者应该实现的回调接口,被 Content 接口的内部实现所调用,用来参与具体实现的逻辑或者事件的监听等。
- App:这部分主要与应用程序或者进程的创建和初始化相关,它被所有的进程使用,用来处理一些进程的公共操作,具体包括两种类型,第一类主要包括进程创建的初始化函数,也就是Content模块的初始化和关闭动作;第二类主要是各种回调函数,用来告诉嵌入者启动完成,进程启动、退出,沙盒模型初始化开始和结束等。
- Browser:同样包含两类,第一类包括对一些HTML5功能和其他一些高级功能实现的参与,因为这些实现需要Chromium的不同平台的实现,同时需要如:Notification、Speech recognition、Web worker、Download、Geolocation等这些Content层提供的接口,Content模块需要调用它们来实现HTML5功能。第二类中的典型接口类是ContentBrowserClient,主要是实现部分的逻辑,被Browser进程调用,还有就是一些事件的函数回调。
- Common:主要定义一些公共接口,这些被Renderer和Browser共享,如一些进程相关、参数、GPU相关等。
- Plugin:仅有一个接口类,通知嵌入者Plugin进程何时被创建。
- Renderer:该部分包括两类,第一类包含获取RenderThread的消息循环、注册V8 Extension、计算JavaScript表达式等;第二类包括ContentRendererClient,主要是实现部分逻辑,被Browser端(或者进程)调用,还有就是一些事件的函数回调。
- Utility:工具类接口,主要包括让嵌入者与Content接口中的线程创建和消息的过滤。
2.2 实践:从Chromium代码结构和运行状态理解现代浏览器
(1)Chrominum Content 接口代码结构
Chromium引入了很多新的特性和功能,除了浏览器外还包括了ChromiumOS和Chrome Frame,这也是新颖的地方。ChromiumOS 就是一个基于Web的操作系统,仅支持Web网页和Web应用程序;Chrome Frame提供一个基于WebKit和Content支持的HTML5的插件。
在上图中的目录,省略了”third_party“,该目录保存了Chromium所依赖的所有第三方开源项目。因为Chromium提供了很多新的特性和功能,这些功能需要很多库来支持,而且这些特性和功能会存在一些不足或者Chromium有特定的需求,所以,Chromium的做法把超过150个项目包含进来,也就是Blink代码页被包含其中。
在目录的后面对代码的功能进行了简单的标注说明,都很简明扼要。重点描述的是Content目录。Content的接口主要在”public“目录中,它包含的目录也是按照进程类型来划分的。
(2)Chromium多进程
通过Chome浏览器和操作系统提供的进程管理工具查看进程情况(略)
(3)Chromium多线程
通过Chome浏览器和操作系统提供的线程管理工具查看进程情况(略)