Chrome 进程模型
为什么要给浏览器使用多进程架构?
传统的浏览器被设计为显示网页,而Chrome的设计目标是支撑“Web App”(当时的js和相关技术已经相当发达了,Gmail等服务也很成功)。这就要求Chrome提供一个类似于“操作系统”感觉的架构,支持App的运行。而App会变得相当的复杂,这就难以避免出现bug,然后crash。同时浏览器也要面临可能运行“恶意代码”。流览器不能决定上面的js怎么写,会不会以某种形式有意无意的攻击浏览器的渲染引擎。如果将所有这些App和浏览器实现在一个进程里,一旦挂,就全挂。
因此Chrome一开始就设计为把隔离性作为基本的设计原则,用进程的隔离性来实现对App的隔离。这样用户就不用担心:
一个Web App挂掉造成其他所有的Web App全部挂掉(稳定性)
一个Web App可以以某种形式访问其他App的数据(安全性)
以及Web App之间是并发的,可以提供更好的响应,一个App的渲染卡顿不会影响其他App的渲染(性能)(当然这点线程也能做到)
因此,这样看起来用进程实现非常自然。
每个进程里都有什么在跑?
Chromium里有三种进程——浏览器、渲染器和插件。
浏览器进程只有一个,管理窗口和tab,也处理所有的与磁盘,网络,用户输入和显示的工作。这就是我们看到的“Chrome界面”。
渲染器开多个。每个渲染器负责处理HTML、CSS、js、图片等,将其转换成用户可见的数据。当时Chrome使用开源的webkit实现这个功能。
顺便说一句,webkit是由Apple开发的,当时有很多坑,也被长期吐槽;现在Chrome已经转成使用自家的Blink引擎了。
插件会开很多。每个类型的插件在第一次使用时会启动一个相应的进程。
总结下,渲染器进程和插件进程就是平时被杀的最多的进程了。
什么时候创建进程?
一般来讲每一个网站的实例都会创建一个渲染进程。但也有特例,比如一个站点通过js在新tab/window上打开同一个站点的另外的页面。这两个界面内部会共享同一个进程,也能彼此分享数据。在Chrome角度,这两个页面算是“同一个App”。但是如果用户用浏览器的地址栏开一个新的tab,而该网址已经有tab了,Chrome会算是“来自同一域名的两个App”,从而创建新的进程。
但是大家都知道进程开多了资源消耗也变大,因此Chrome会限制最大的进程数量(比如20)。当进程达到这个数量后,Chrome会倾向于去复用已有的进程(所以这时,隔离性就会被影响)。
我看了一下Chrome的任务管理器,发现现在Chrome可以启动不止20个进程,大概是因为现在的计算机性能比当时强很多,所以可以吃更多资源。
根据这篇文章,Chromium实际支持多种进程模型。
每个站点的实例创建一个新的进程
每个站点创建一个新的进程(根据domain)
每个Tab创建一个新的进程
单进程
浏览器在现实中可以根据场景去灵活调整怎么创建新的进程。
总结
从这个Blog中我能总结出两点特别重要的地方:一个是工程中的思维方式。
Junior的程序员可能会这么思考:直觉上线程比进程高级,轻量,性能也更好,所以应该用多线程(从技术的主观感觉推断出方案);
Senior的程序员或者架构师会这么思考:想解决的是复杂系统中的安全和错误处理的问题。为了解决它们,最好的办法就是严格隔离。而实现隔离最自然的方式就是用多进程。但多进程有一些问题,所以我要这样这样这样去设计,保证能用多进程同时还能最大程度避免那些问题(从问题的重要程度找到最核心的方案,再围绕核心方案分治去处理小问题)。
所以你能理解为什么Junior是Junior。Junior成长为Senior的先决条件就是多理解每一个技术点的利和弊,适用场景;以及如何找到一个复杂问题里最关键的问题是什么。
第二点是处理错误的方法。一般人说“稳定性”的第一反应是“兜底”。即不管什么问题,我要兜住让系统可以继续往下运行,只要不挂就行。但实际上是大错特错的。出现程序不能处理的异常,那么很可能程序接下来做的事情就不可控了,比如乱写磁盘文件,乱发非法请求。这对于浏览器这种系统是完全不可以接受的。对这样的问题的出现就必须“fast-fail“,也就是立刻Crash。所以我们能看到iOS App闪退,桌面程序挂掉,以及操作系统蓝屏。
但,的确,这样做会让体验不好,因此首先要保证工程质量在设计时就把尽可能多的错误处理考虑进去,并用测试等手段确保不出问题。
但即便如此,问题也无法100%的解决,总有意料之外的情况发生,因此要想办法去限制Crash的范围。这也是隔离存在的意义。为了实现高可靠的系统,必然要按照某种方式进行隔离,把高度危险的,可控度不高的部分隔离出去,避免其Crash伤及系统的其他部分。
Chrome 多进程模型的特点
稳定性 进程之间完全隔离,单个进程的崩溃,不会影响其他进程
安全性 进程间数据不共享,保证安全性
更加合理、有效的内存使用 关掉进程,释放进程占用的所有资源,不存在内存碎片
Last updated
Was this helpful?