前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >浏览器渲染(进程视角)

浏览器渲染(进程视角)

原创
作者头像
醉酒鞭名马
修改2020-05-20 12:49:37
2.6K2
修改2020-05-20 12:49:37
举报

文章所介绍内容基于chrome浏览器,当我们打开一个网页时,观察任务管理器,会发现有大于4个进程,浏览器进程,GPU进程,网络进程,标签页面进程,插件进程,为什么是这么多进程?

这篇文章主要站在浏览器进程架构模型的发展史来聊一下页面的渲染,及渲染过程中,进程之间是如何通信的。

任务管理器
任务管理器

这些进程的职责是什么,怎么来的呢?为什么这么设计呢?

1 浏览器进程模型的演进

要了解进程架构演进,需要先了解进程的几个特点:

  • 程序的最小运行环境进程:一个程序运行时,需要执行环境,包括执行上下文、代码、数据,和一个执行任务的主线程。在程序启动时,操作系统为程序会分配一块内存空间来初始化这样的运行环境,这样的一个运行环境称之为进程
  • 线程负责执行任务:程序的执行最终是在进程中的线程内执行的
  • 线程由进程管理:进程中的线程是不能独立存在的,由进程来管理
  • 线程阻塞:进程中的线程是阻塞的(如果此线程操作了公共内存区),任意线程执行出错都会导致进程崩溃
  • 数据共享:线程之间共享进程中的数据
  • 进程隔离:进程之间是相互隔离相互独立的内存空间,可以通过IPC通信
  • 内存回收:进程运行中,可以手动或自动控制内存的回收,或者在进程关闭之后,操作系统会回收内存,已达到内存循环利用的效果

1.1 单进程架构

06年及以前,早起的架构都是单进程架构

单进程模型
单进程模型

早期单进程架构是页面渲染和网络下载都是运行在同一个浏览器主进程中,而dom/com解析,js脚本执行,图像输出,插件运行都运行在同一个线程中,这样也带来了一系列的问题:

  • 不稳定:渲染主线程内的js脚本,或插件运行出错会导致整个进程崩溃,导致浏览器崩溃
  • 不流畅:主线程同一时间只能运行一个模块,要循环执行各任务,插件、或脚本的死循环及独占线程,长时间运行内存不能回收,导致程序卡顿变慢
  • 不安全:插件通常c/c++编写代码,因为在浏览器进程中,浏览器需要操作系统资源,所以插件有可能会有往操作系统恶意注入病毒的风险

1.2 多进程架构

为了以上问题,第一版多进程架构模型出现

第一版多进程模型
第一版多进程模型

为了解决单进程问题,将渲染进程按照任务类型进行拆分,将主要影响浏览器进程稳定和安全的插件任务,渲染任务拆分出独立的进程,并且将剥离出来的插件进程、渲染进程封装在沙箱中,操作系统资源需要经过浏览器进程层操作。

1.3 目前多进程架构

开篇的任务管理器展示的图片,打开一个标签页有4个以上的进程,这是为什么呢?

随着浏览器承载的内容越来越丰富,对于展现的形式也越来越酷炫,很多动画效果、3D效果、及浏览器本身UI功能也越来也丰富,这导致浏览器主进程也承载着更多的任务,所以为了浏览器的稳定性考虑,进一步进行拆分

现在的多进程模型
现在的多进程模型

这个就解释了我们打开一个标签页面的时所用看到的进程数量为什么4个以上了,插件进程和渲染进程是多个的,而网络进程和GPU进程是独立的进程,由浏览器主进程管理维护,进程之间的通信和对操作系统资源的操作都由浏览器主进程管理,这样也就让浏览器主进程的职责划分的更加清晰

问题:

  • 更高内存消耗:为了稳定性、安全把插件进程,和渲染进程拆分出来独立运行多份,这也意味着每个进程都会独立的运行空间,对内存的消耗会更大,每个进程都包括独立的公共基础结构的副本(例如js、blink的运行环境)
  • 模块依赖高:浏览器主进程的职责依然很重要,包括了很多与操作系统之间的交互,及渲染进程、插件进程、网络进程、GPU进程之间的模块依赖
  • 多系统的支持不高:浏览器进程封装了太多与操作系统的功能,如果要换一个操作系统、换一个设备,依然能够让chrome稳定运行如何做到呢?

1.4 面向服务架构

面向服务的模型
面向服务的模型

这有些关键字:更内聚、松耦合、易维护和扩展,简单、稳定、高速、安全、多设备,其他的可以自己思考一下了,这不就是一个小型的操作系统要做的事情么,目前很多微服务的、微前端架构的思想都是如此,面服务是目前主流的思想。


2 标签页和进程的关系

以上从浏览器进程模型演进介绍了打开一个页面所启动的进程数量,接下来我们从标签页来分析下进程数量。

2.1 概念理解

  • 同源站点:同源是指,协议,域名,端口完全相同才算是同源,其中有一项不同,则为跨域。
  • 相同站点:同站点是指协议和根域名相同,既为相同站点,如http://local.test.com,http://beta.test.com,http://test.com:8000,以上均为同一站点,http://other.com,http://test.com 协议相同、根域名不同为不同站点
  • 浏览上下文:一个标签页所包含的内容,window对象,浏览历史,滚动条位置等信息
  • 浏览上下文组:通过脚本可以把浏览上下文关联起来,称之为浏览上下文组

为了演示我们简单用apache创建几个站点

代码语言:javascript
复制
<VirtualHost *:80>
	DocumentRoot "D:/workspace/learn/test/other.com"
	DirectoryIndex index.html
	ServerName other.com
	ServerAlias other.com
</VirtualHost>

<VirtualHost *:80>
	DocumentRoot "D:/workspace/learn/test/test.com"
	DirectoryIndex index.html
	ServerName test.com
	ServerAlias test.com
</VirtualHost>


<VirtualHost *:80>
	DocumentRoot "D:/workspace/learn/test/local.test.com"
	DirectoryIndex index.html
	ServerName local.test.com
	ServerAlias local.test.com
</VirtualHost>


<VirtualHost *:80>
	DocumentRoot "D:/workspace/learn/test/beta.test.com"
	DirectoryIndex index.html
	ServerName beta.test.com
	ServerAlias beta.test.com
</VirtualHost>

我们看下下面几种打开网站的方式

2.2 新标签页单独打开(独占一个进程)

新标签页打开,浏览器会默认为其创建一个独立的渲染进程,每个站点不管是不是同一站点,均为其创建一个渲染进程

可以看到下图,每个标签页面,对应一个进程id

新标签页面打开
新标签页面打开

2.3 在一个标签页,使用iframe打开各个站点

可以看到使用iframe方式打开的页面,同一站点共用了一个渲染进程,这是因为在一个标签页内使用iframe的方式打开页面,其子页面和父页面之间建立连接关系,并且是同一站点的则会默认共用一个渲染进程

如下图:

iframe打开
iframe打开

2.4 通过父页面的a标签打开

当在主页面test.com中已<a> 标签的方式在新标签中打开页面是,同一站点也共用一个渲染进程,那是因为使用a标签打开新页面的方式,也会为站点之间创建连接,使用同一浏览上文组

代码语言:javascript
复制
<body>
    test.com <br>
    <a target="_blank" href="http://test.com/index4.html">http://test.com/index4.html</a> <br>
    <a target="_blank" href="http://local.test.com">http://local.test.com</a> <br>
    <a target="_blank" href="http://beta.test.com">http://beta.test.com</a><br>
    <a target="_blank" href="http://other.com">http://other.com</a><br>
</body>
使用a标签链接打开页面
使用a标签链接打开页面

通过上图可以得到如下结论:

  • a标签页面打开可以将页面之间创建连接。拥有同一浏览上下文组
  • 同源站点和同一站点共用父页面的渲染进程
  • 不同站点会新创建一个渲染进程

我们再来进一步看一下,具有同一浏览上下文组的页面,如何在脚本中体现之间关系。还是上面的图

-- 切换到test.com页面(test.com页面是由test.com页面中a标签打开),在控制台查看window.opener

会发现同源站点,具有同一浏览上下文组,子页面可以使用window.opener.document操作父页面dom

同源站点的opener对象
同源站点的opener对象

-- 我们切换到local.test.com页面(local.test.com页面是由test.com页面中a标签打开),在控制台查看window.opener

会发现同站点,使用同一浏览上下文组,依然不能够使用window.opener.document操作父页面的dom,这是浏览器同源策略的限制,opener对象会查看到浏览上线文组js对象

同一站点opener对象
同一站点opener对象

-- 我们切换到other.com页面(other.com页面是由test.com页面中a标签打开),在控制台查看window.opener

会发现不同站点,具有同一浏览上下文组,子页面不可以使用window.opener.document操作父页面dom和js对象

不同站点的opener对象
不同站点的opener对象

-- 我们切换到local.test.com页面(local.test.com页面是在新标签页中打开),在控制台查看window.opener发现为null

会发现在不同进程中,不具有同一浏览上下文组的页面之间是相互隔离的

页面之间无连接,不同进程中opener对象
页面之间无连接,不同进程中opener对象

-- 如果我们在a标签中添加rel=noopener属性,那么和在新标签页单独打开站点是一样的,noopener这个属性是告诉与当前父业面不共用浏览上下文组

代码语言:javascript
复制
<body>
    test.com <br>
    <a target="_blank" rel="noopener" href="http://test.com/index4.html">http://test.com/index4.html</a> <br>
</body>

test.com/index5.html中使用a标签,rel="noopener"属性时,打开的页面进程使用情况如下

使用rel="noopener" 属性的opener对象
使用rel="noopener" 属性的opener对象

总结


两个站点使用同一渲染进程的两个条件

  • 两个站点必须属于同一站点
  • 两个站点之间要有连接关系(同一浏览上下文组)

  • 建立浏览上下文组的三种方式:
    • 1 a标签打开
    • 2 window.open()脚本打开
    • 3 iframe方式打开
  • 同源站点具有同一浏览上下文组的页面之间:子页面可以用window.opener操作父页面的dom
  • 同一站点具有同一浏览上下文组的页面之间:子页面不可以用window.opener操作父页面的dom
  • 不同站点具有同一浏览上下文组的页面之间:window.opener无法查看父业面dom

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1 浏览器进程模型的演进
    • 1.1 单进程架构
      • 1.2 多进程架构
        • 1.3 目前多进程架构
          • 1.4 面向服务架构
          • 2 标签页和进程的关系
            • 2.1 概念理解
              • 2.2 新标签页单独打开(独占一个进程)
                • 2.3 在一个标签页,使用iframe打开各个站点
                  • 2.4 通过父页面的a标签打开
                  • 总结
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档