浏览器相关-模型

2019-06-30 14:01:5010/1/2021, 3:34:43 AM

浏览器是多进程的

浏览器包含的进程

  • browser 进程--浏览器的主进程,负责协调,主控,有且只有一个
    • 负责浏览器界面的显示,与用户交互(前进,后退等)
    • 负责各个页面的管理,创建和销毁其他进程
    • 将 render 进程得到的内存中的 bitmap 绘制到用户界面上
    • 网络资源的下载与管理等
  • 第三方插件进程--每种类型的插件对用一个进程,仅当使用该插件时创建
  • GUI 进程--最多只有一个,用于 3D 图形的绘制
  • render 进程(浏览器内核),默认为一个 tab 页一个进程,互不影响
    • 页面渲染,脚本执行,事件处理等

浏览器使用多进程的优势

  • 避免单个 page crash 后影响到整个浏览器
  • 避免第三方插件 crash 后影响整个浏览器
  • 多进程充分利用多核优势
  • 方便使用沙盒模型隔离插件等进程,提高浏览器稳定性

render 进程

常驻线程

  • GUI 渲染线程
    • 负责渲染浏览器界面,解析 HTML,CSS,构建 DOM 树和 RenderObject 树,布局和绘制等
    • 当界面需要重绘(repaint)或由于某种操作引发回流(reflow)时,该线程就会执行
    • GUI 线程与 JS 引擎线程是互斥的,当 JS 引擎执行时 GUI 线程就会被挂起,GUI 更新会被保存在一个队列中等到 JS 引擎空闲时立即执行
  • JS 引擎线程
    • 也称为 JS 内核,负责处理 JavaScript 脚本程序(例如 V8 引擎)
    • JS 引擎线程负责解析 JavaScript 脚本,运行代码
    • JS 引擎一直等待着任务队列中任务的到来,然后加以处理,一个 tab 页(render 进程)中无论何时只有一个 js 线程在运行 js 程序
    • GUI 线程与 js 引擎线程时互斥的,如果 js 执行时间过长,这样就会造成页面渲染不连贯,导致页面渲染加载阻塞
  • 事件触发线程
    • 归属于浏览器而不是 JS 引擎,用来控制事件循环
    • 当 js 引擎执行代码块如 setimeout 时(也可来自浏览器内核的其他线程,如鼠标点击、Ajax 异步请求等),会将对应任务添加到事件线程中
    • 当对应的事件符合触发条件时该线程会把事件添加到待处理队列的队尾,等待 JS 引擎的处理
  • 定时器触发线程
    • 传说中的 setInterval 与 setTimeout 所在线程
    • 浏览器定时计数器并不是由 JavaScript 引擎计数的,(因为 JavaScript 引擎是单线程的, 如果处于阻塞线程状态就会影响记计时的准确)
    • 因此通过单独线程来计时并触发定时(计时完毕后,添加到事件队列中,等待 JS 引擎空闲后执行)
    • 注意,W3C 在 HTML 标准中规定,规定要求 setTimeout 中低于 4ms 的时间间隔算为 4ms。
  • 异步 http 线程
    • 在 XMLHttpRequest 在连接后是通过浏览器新开一个线程请求
    • 将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件,将这个回调再放入事件队列中。再由 JavaScript 引擎执行。

browser 进程与 render 进程之间的通信

  • Browser 进程收到用户请求,首先需要获取页面内容(譬如通过网络下载资源),随后将该任务通过 RendererHost 接口传递给 Render 进程
  • Renderer 进程的 Renderer 接口收到消息,简单解释后,交给渲染线程,然后开始渲染
  • 渲染线程接收请求,加载网页并渲染网页,这其中可能需要 Browser 进程获取资源和需要 GPU 进程来帮助渲染
  • 当然可能会有 JS 线程操作 DOM(这样可能会造成回流并重绘)
  • 最后 Render 进程将结果传递给 Browser 进程
  • Browser 进程接收到结果并将结果绘制出来

浏览器内核(render 进程)中线程的关系

GUI 渲染线程与 JS 引擎线程互斥

由于 JavaScript 是可操纵 DOM 的,如果在修改这些元素属性同时渲染界面(即 JS 线程和 UI 线程同时运行),那么渲染线程前后获得的元素数据就可能不一致了。 因此为防止渲染出现不可预期的结果,浏览器设置 GUI 渲染线程与 JS 引擎为互斥,当 JS 引擎执行时 GUI 线程会被挂起。 GUI 更新则会被保存在一个队列中等到 JS 引擎线程空闲时立即被执行。

JS 引擎阻塞

web worker 线程

为解决上一个问题而提出

  • 创建 worker 时,JS 引擎会向浏览器申请开一个子线程
  • JS 引擎线程与 worker 线程间通过特定的方式通信(postMessage API),需要通过序列化对象来与线程交互特定的数据 所以,如果有非常耗时的操作,可以单独打开一个 worker 线程,这样不管如何天翻地覆都不会影响 JS 引擎主线程,只待计算出结果后,将结果通信给 JS 主线程即可

shared worker 线程

web worker 与 shared worker 的区别

  • web worker 只属于某个页面,不会和其他页面的 render 进程共享
  • shared worker 是浏览器所有页面共享的,不能采用与 worker 同样的方式实现,不属于某个 render 进程
  • web worker 相当于“线程”,shared worker 相当于“进程”