多进程架构
Chromium 的每个标签页都是一个独立的进程,为了确保安全,Chromium 会限制每个渲染引擎彼此之间的访问权限,以及他们访问系统其他资源的权限。
运行 UI 和管理 Tab/Plugin 的主进程称为”浏览器进程” 或 “浏览器(Browser)”,标签页相关的进程被称作”渲染线程”或”渲染器(renderer)”
renderer 使用 Blink 开源引擎来实现解析和 HTML 布局
Managing render processes
每个渲染进程有一个全局的 RenderProcess 对象,该对象管理渲染进程与父浏览器进程之间的通信,并维护全局状态。浏览器为每个渲染进程维护一个对应的RenderViewHost,用来管理浏览器状态,并与渲染器通信。浏览器与渲染器使用Chromium’s IPC system进行通信。
Managing views
每个渲染进程都有一个或多个 RenderView 对象,由 RenderProcess
管理(它与标签页的内容相关)。相应的RenderProcessHost
维护一个与渲染器中每个view相关的RenderViewHost
。每个view被赋予一个view ID,以区分同一个渲染器中的不同view。这些 ID 在一个渲染器中是唯一的,但在浏览器中不是唯一的,所以区分一个view需要一个RenderProcessHost和一个view ID。
浏览器与特定标签页之间的通信是通过RenderViewHost对象来完成的。
组件与接口
In the render process:
- RenderProcess通过浏览器中和其对应的RenderProcessHost来处理 IPC。每个渲染进程只有一个RenderProcess对象。所有浏览器-渲染器之间都采用这种方式通信。
- RenderView 对象与浏览器进程中相应的 RenderView 和 WebKit 嵌入层通信(通过RenderProcess)。这个对象代表了一个网页在标签页或一个弹出窗口的内容。
In the browser process:
- Browser 对象代表了顶级浏览器窗口
- RenderProcessHost 对象代表了浏览器端的浏览器与渲染器的IPC连接。在浏览器进程里,每个渲染进程有一个RenderProcessHost对象。
- RenderViewHost 对象封装与远程 RenderView 的通信,RenderWidgetHost 在浏览器中处理输入和 RenderWidget的绘制。
共享渲染过程
通常,每个新的 window 或标签页是在一个新进程里打开的。浏览器会生成一个新的进程,然后指导它去创建一个RenderView。但有时候在标签页或窗口之间共享渲染进程是有必要的。
这些策略在Process Models里有阐述。
检测 Crash OR 异常渲染
每个和浏览器进程通信的 IPC 都会检测进程的句柄,如果句柄是 signaled,则代表渲染进程 Crashed 相应的标签页也会收到 Crash 的信息。这是 Chromium 将显示一个 sad tab 屏幕,通知用户渲染器已崩溃,该页面可以通过按重新加载按钮或启动新的导航来重新加载。
Sandboxing the renderer
renderer的进程被沙箱化,其权限进行了如下的限制:
- 渲染器只能通过其父浏览器进程访问网络
- 主机操作系统的内置权限限制了它对文件系统的访问
- rederer 进程运行在单独的Windows桌面上,该桌面对用户是不可见的。
- 限制对用户的display 和 related对象的访问
参考
[1] https://www.cntofu.com/book/101/zh/Start_Here_Background_Reading/Multi-process_Architecture.md
如何显示网页
应用概念层
每个矩形代表一个应用概念层,每层之间互不相通,层与层之间也没有任何依赖关系。
- WebKit:Safari,Chromium和其他所有基于 WebKit 的浏览器共享的渲染引擎。WebKit Port是WebKit的一个部分,用来集成平台独立的系统服务,比如资源加载与图像。
- Glue: 将WebKit类型转换为Chromium类型。该层是 WebKit 的嵌入层。
- Renderer / Render host: Chromium 多进程架构的嵌入层,代理通知,跨进程边界执行命令。
- WebContents:一个可重用的组件,它是 Content 模块的主类。它易于嵌入,允许多进程将HTML绘制成View。
- Browser: 浏览器窗口,它包含多个 WebContentses。
- Tab Helpers: 可以附加到WebContents的各个对象(通过WebContentsUserData Mixin)。浏览器将这些独立对象中的一种绑定到WebContent给它持有(一个用于Favicons,一个用于infobars)
The render process
Chromium 的渲染进程通过 glue接口将 WebKit 端口嵌入。它的工作主要是作为渲染器端到浏览器的IPC通道。
reder 中最重要的类是RenderView
,在 /content/renderer/render_view_impl.cc
。这个对象代表一个 web 网页,处理浏览器进程之间的所有导航相关命令,派生于RenderWidget
(RenderWidget 提供绘图和输入事件处理)。RenderView 通过全局 RenderProcess 对象与浏览器进程通信。
RenderWidget 和 RenderView 之间的区别:
RenderWidget 通过 glue 层(WebWidgetDelegate)的抽象接口映射到一个WebCore::Widget对象。这基本是屏幕上的一个窗口,用于接收输入事件并在其中进行绘制。
RenderView 继承自 RenderWidget,代表标签页或弹窗的内容。除了绘制与组件输入事件外,它还处理导航指令。只有一种情况下,RenderWidget可以在没有RenderView时存在,就是网页中的下拉选择框(select box)。下拉选择框必须用native window来渲染(因为其没有独立 web 网页)。
Threads in the renderer
每个渲染器都有两个线程,The render thread is where the main objects such as the RenderView and all WebKit code run。当渲染器线程与浏览器通信时,消息首先被发送到主线程,然后主线程将消息发送到浏览器进程。除了这种方式,同样还允许我们从渲染器同步地向浏览器发送消息,这种情况一般发生在,需要浏览器返回的结果才能继续执行的事件。例如,在 JavaScript 请求获取页面的cookie时,渲染器线程会阻塞,主线程将对接收到的所有消息排队,直到找到正确的响应。在此期间接收到的任何消息,在这之后都会分配到渲染器线程以进行正常处理。
The browser process
Low-level browser process objects
所有 IPC 与渲染进程的通信都通过浏览器的 I/O线程来完成的,此线程还处理所有网络通信,以防止其干扰用户界面。
当一个 RenderProcessHost 对象在主线程(用户界面运行的地方)完成初始化时,它会创造新的渲染器进程和一个ChannelProxy IPC对象(具有一个命名了的管道通向渲染器),自动转发所有的消息回给UI线程的RenderProcessHost。该对象运行在浏览器的 I/O 线程上,监听渲染器的命名管道,并自动将所有消息转发回 UI 线程上的RenderProcessHost。ResourceMessageFilter 也将安装在此通道中,它将过滤掉某些可以直接在I/O线程上处理的消息,如网络请求。筛选的过程发生在 ResourceMessageFilter::OnMessageReceived
。
UI线程中的RenderProcessHost负责分发所有view相关消息给合适的 RenderViewHost(它自己处理有限数量的与非View特定的消息)此调度发生在RenderProcessHost::OnMessageReceived中。
High-level browser process objects
View-specific 信息源于RenderViewHost::OnMessageReceived。大多数消息都在这里处理,其余的转发到RenderWidgetHost基类。这两个对象映射到渲染器中的 RenderView 和 RenderWidget,每个平台都有一个View类以集成到native view系统。
在RenderView / Widget上方是 WebContents 对象,大部分的消息结束于这个对象的函数调用。WebContents代表网页的内容,它是内容模块中的顶级对象,负责在一个矩形的view中展示网页。
WebContents 对象包含在 TabContentsWrapper 中。在chrome /
中,负责一个标签页。
参考
[1] https://www.chromium.org/developers/design-documents/displaying-a-web-page-in-chrome
Threading and Tasks in Chrome
Chrome 是基于多进程架构的,每个进程又是多线程的。多进程架构主要目的是为了让主线程(例如 Browser 进程中的 UI 线程)和 IO 线程(进程中处理 IPC 消息的线程)保持快速响应。Chromium希望尽量保持UI处于响应状态。为此遵循如下设计原则:
- 不在UI线程上执行任何阻塞I/O操作,以及其它耗时操作。
- 少用锁和线程安全对象
- 避免阻塞I/O线程
- 线程之间不要互相阻塞
- 在数据准备好更新到共享缓冲时才用锁(在准备数据期间不要用锁)
核心概念
- Task: task 是一个待处理的工作单元,也可以理解为是具有可选关联状态的函数指针。In Chrome this is
base::OnceCallback
andbase::RepeatingCallback
created viabase::BindOnce
andbase::BindRepeating
, respectively. (documentation). - Task queue: 待处理的任务队列。
- Physical thread: 操作系统提供的线程 (e.g. pthread on POSIX or CreateThread() on Windows). The Chrome cross-platform abstraction is
base::PlatformThread
. base::Thread
: A physical thread 永远处理来自专用任务队列的消息,直到 Quit()。- Thread pool: 具有共享任务队列的物理线程池. In Chrome, this is
base::ThreadPoolInstance
. - Sequence or Virtual thread: chrome 管理的执行线程。与 physical thread 一样,在任何给定时刻只有一个任务可以在给定的 sequence / virtual thread 线程上运行,并且每个任务都可以看到前面任务的副作用。 任务会按顺序执行,但可能会在每个physical thread之间跳转。
- Task runner: 一个可以发布任务的界面. In Chrome this is
base::TaskRunner
. - Sequenced task runner: 任务运行程序,它保证发布到它的任务将按发布顺序运行。每个这样的任务都能看到它之前的任务的副作用,发布到序列任务运行器的任务通常由单个线程(virtual or physical)处理。 In Chrome this is
base::SequencedTaskRunner
which is-abase::TaskRunner
. - Single-thread task runner: 一个有序的任务运行程序,它保证所有任务都由相同的物理(physical)线程处理。 In Chrome this is
base::SingleThreadTaskRunner
which is-abase::SequencedTaskRunner
.
Threads
每个 chrome 的进程都包含如下线程:
- 主线程
- in the browser process (BrowserThread::UI): updates the UI
- in renderer processes (Blink main thread): runs most of Blink
- IO 线程
- 在所有进程中:所有IPC消息都到达该线程,处理消息的应用程序逻辑可能位于不同的线程中(IO线程可能将消息路由到绑定到不同线程的Mojo接口)
- 大多数异步 I/O 都发生在这个线程上 (base::FileDescriptorWatcher).
- 浏览器进程下是
BrowserThread::IO
- 一些专用线程
- 一个通用线程池
大多数线程都有一个循环,用以从队列中获取任务并运行它们(队列可以在多个线程之间共享)。
Tasks
task 是用以异步执行的工作单元,由base::OnceClosure
添加到队列。base::OnceClosure
存储着函数指针和参数,通过 Run()
来调用所存储的函数指针。base::OnceClosure
通过base::BindOnce
来声明。
1 | void TaskA() {} |
执行方式
- 并行(Parallel):没有任务执行顺序,可能在任何线程上执行所有任务(通过线程池实现)
- 顺序执行(Sequenced):在任何线程上,按照提交顺序一次执行一个任务(通过
SequencedTaskRunner
实现) - 单线程(Single Threaded):按发送顺序执行的任务,在单个线程上一次执行一个任务 (通过
SingleSuqenceTaskRunner
实现)- COM Single Threaded:COM已初始化的单线程的变体。
参考:
[1] https://chromium.googlesource.com/chromium/src/+/lkgr/docs/threading_and_tasks.md