從「快穩省安全」看Chromium——Chromium學習系列

前言

對於不一樣的角色,瀏覽器有着不一樣的意義:css

普通用戶經過它來瀏覽網頁,在意的是穩定,高效和安全;html

前端開發同窗關心的是兼容性和性能優化。咱們能夠經過學習Chromium的內部機制,更好的理解各類優化手段。前端

泛泛的介紹意義不大,咱們從 快/穩/省/安全 這幾個維度切入,看看瀏覽器是如何作到這幾點的,這裏以chromium爲例,畢竟開源,且資料豐富。html5

加載快

Chromium拿到頁面請求後,首先對須要請求的資源作分類,而後根據相關的安全策略,對資源作權限校驗,接着對加載順序按優先級排序,再根據順序進行加載。node

Chromium支持的資源分爲以下14類:python

類型git

介紹github

kMainResourceweb

即主資源,html頁面文件資源就屬於該類型,包括location即導航輸入地址獲得的頁面、使用frame/iframe嵌套的、經過超連接點擊的頁面以及表單提交這幾種ajax

kImage

圖片資源

kCSSStyleSheet

css資源

kScript

腳本資源,例如js資源

kFont

字體資源,例如網頁中經常使用的字體集.woff資源

kRaw

混合類型資源,最多見的ajax請求就屬於這類資源

kSVGDocument

SVG可縮放矢量圖形文件資源

kXSLStyleSheet

擴展樣式表語言XSLT

kLinkPrefetch

HTML5頁面的預讀取資源(Link prefetch),例如dns-prefetch

kTextTrack

video的字幕資源,- 即<track>標籤

kImportResource

HTML Imports,將一個HTML文件導入到其餘HTML文檔中,例如<link href="import/post.html" rel="import" />

kMedia

多媒體資源,video,audio都屬於該類資源

kManifest

HTML5 應用程序緩存資源

kMock

預留的測試類型

資源的權限校驗這塊,放到安全那一節介紹。

Chromium對資源的優先級是這樣定義的:

能夠看到優先級總共分爲五級:very-high、high、medium、low、very-low,其中頁面、CSS、字體這三個的優先級是最高的,而後就是Script、Ajax這種,圖片、音視頻的默認優先級是比較低的,最低的是prefetch的資源。

資源按照優先級來下載,於體驗有很大的幫助。

Prefetch(預加載)是個有意思的東西,有時候你可能須要讓一些資源先加載準備好。

例如用戶輸入出錯的時候在輸入框右邊顯示一個X的圖片,若是等要顯示的時候再去加載就會有延時,這個時候能夠用一個link標籤:

<link rel="prefetch" href="image.png">複製代碼

瀏覽器空閒的時候就會去加載。另外還能夠預解析DNS:

<link rel="dns-prefetch" href="https://cdn.test.com"> 複製代碼

預創建TCP鏈接:

<link rel="preconnect" href="https://cdn.chime.me">複製代碼

資源緩存

資源的緩存是瀏覽器優化加載的重要手段。加載後的資源會存入資源緩存池,當chromium須要請求資源的時候,會優先從緩存池中查找,查找的key就是資源的URL。

資源緩存池使用LRU算法管理其中的資源,若是是強緩存s,又能命中cache的話,則不發送請求,若是是協商緩存,則仍向服務端發送請求。

多進程資源加載

Chromium採用的是多進程資源加載的機制:


Render進程(對應的是tab)沒有權限下載資源,處於安全和效率的考量,Render進程的資源獲取其實是經過進程間通訊(IPC)交給Browser進程(對應整個瀏覽器)來完成,Browser進程有獲取本地或網絡資源的權限。

這種架構的一個優勢是,全部的資源請求都徹底在I/O線程上處理,用戶接口產生的活動與網絡事件之間互不干擾。資源過濾器運行在Browser進程中的I/O線程中,截獲資源請求消息,並將其轉發給Browser進程中的ResourceDispatcherHost單例。

這個單例接口容許瀏覽器控制各個Render進程的網絡訪問權限,它還能實現高效和一致性的資源共享,例如:

1. socket池和鏈接限制:瀏覽器可以對每一個profile、代理和{scheme, host, port}組所對應的已開啓socket數量進行限制(分別爲25六、32和6個)。注意,按照這個規則,同一{host, port}最多能夠進行6個HTTP和6個HTTPS鏈接。

2. socket重用:持久性的TCP鏈接會在請求處理以後在socket池中保留一段時間,以供鏈接重用,這樣可以避免發起新的鏈接額外帶來的DNS、TCP和SSL(若有須要)的啓動開銷。

3. socket後期綁定:當socket準備好分派應用程序請求時,請求才與基礎的TCP鏈接綁定起來,這樣一來能夠得到更好的請求次序優化(例如:當socket在鏈接中時,更高優先級的請求到達),更大的流量(例如:在現有socket可用而新鏈接正在打開時,重用「剛使用過」的TCP鏈接)以及TCP預鏈接的通用機制和其餘一些優化。

4. 一致的會話狀態:全部Render進程的身份鑑證、cookies和緩存數據都是共享的。

5. 全局性資源和網絡優化:瀏覽器能夠從全部Render進程和未完成請求的全局作出決策。例如,對前景標籤頁發起的請求賦予網絡優先級。

6. 預測性優化:經過觀測全部的網絡流量狀況,Chromium可以構建和修正預測性模型提高性能。

就Render進程而言,它只是經過IPC發送資源請求消息,這個請求被打上對應Browser進程的惟一請求ID,剩下的部分都是由瀏覽器內核進程處理的。

啓動快

影響頁面渲染效率的因素很多,Chromium對此作了很多優化。

V8引擎的優化

2017年,V8作了重大升級,用上了Ignition(點火器) + TurboFan(增壓渦輪)。

這樣作的目的一是下降內存佔用,二是減小啓動時間,三是下降複雜度。


Composite

Composite是一種技術,Chromium會把頁面分紅多層,開啓多個Compositor線程,分別對它們進行光柵化。當頁面有滾動發生時,由於各層已經作了光柵化,因此要作的就是合成新的幀。

爲了找出哪些元素屬於哪一層,主線程會根據Layout Tree生成Layer Tree。若是你但願某個元素分層渲染,能夠經過設置will-change這個樣式屬性,提醒瀏覽器這麼作。


一旦建立了層樹並肯定了繪製順序,主線程就會將該信息提交給compositor(合成器)線程。 compositor線程隨後柵格化各層。 一個圖層可能像頁面的整個長度同樣大,所以合成器線程將它們分紅多個圖塊並將每一個圖塊發送到光柵線程。 柵格線程柵格化每一個圖塊並將它們存儲在GPU內存中。


compositor線程爲不一樣的圖塊分配不一樣的優先級,以便視口內的部分優先被光柵化。 圖層還具備多個不一樣分辨率的拼接,以應付放大縮小等操做。

使用GPU

Chromium 利用 GPU 的最初的動力是 3D CSS,除了給 3D CSS 帶來加速,GPU 一樣能夠用於提高普通的頁面的渲染。這個理念在 Chromium 中獲得了實現。首先 Chromium 將 GPU 加速運用到了 video canvas 等標籤的渲染上,另外爲高效合成 z 軸方向重疊元素,引入了圖像合成的概念。

Chromium 利用 GPU 解決了這幾個影響性能的問題:

1. 把部分光柵化的任務交給 GPU,下降繪製使用的時間(每幀從 100ms 下降到 4-5ms),爲 JavaScript 的執行爭取了更多的時間

2. 利用合成器,可讓一些 CSS 動畫徹底在 GPU 中繪製(Compositor Thread),不須要 CPU 的干預(Main Thread),即使被 JavaScript 阻塞也能保持動畫流暢

3. 圖層之間互不影響,減小了發生 reflow repaint 時所要遍歷的元素數量

Chromium 在其多進程架構上引入了 GPU 進程。這個模型是能夠伸縮的,在一些性能較低的平臺上,GPU 進程可能會降爲 GPU 線程。渲染進程對 GPU 的訪問,會以指令的形式發送到 CommandBuffer(它是渲染進程和 GPU 進程共享的內存區域),而後經過 IPC 告知 GPU 進程。大致來講,比起 IPC 帶來的損耗,這個架構帶來的收益更加突出。由於絕大部分指令不須要返回值,這讓渲染進程能夠當即返回,並繼續處理其餘渲染任務。另外,將渲染進程隔離在不能直接訪問 GPU 的安全沙盒中,這對 Chromium 提供 native 擴展的場景顯得尤爲重要。

運行流暢

多線程模型

頁面運行不流暢的表現主要是janky(跳幀),做爲前端同窗,多少都知道一些關於如何避免跳幀,卡頓的優化作法,固然瀏覽器在這上面也作了很多改進。

爲了解決頁面卡頓的問題,Chromium使用基於異步通訊的多線程模型。也就是說,一個線程請求另一個線程執行一個任務的時候,不須要等待該任務完成就能夠去作其它事情,以此避免卡頓。

事件的優化

通常咱們屏幕的刷新速率爲 60fps,但某些事件的觸發會高於這個頻率,出於優化的目的,Chromium 會合並連續的事件(如 wheel, mousewheel, mousemove, pointermove, touchmove ),並延遲到下一幀渲染時候執行。

而keydown,keyup,mouseup,mousedown,touchstart和touchend 這些非連續事件則會當即被觸發。



多進程模型

以前介紹多進程資源加載的時候,簡單提到過Render進程和Browser進程,爲了保證瀏覽器的穩定性,Chromium採用多進程的架構,將Render,Plugin和GPU進程與Browser進程分隔開來,這幾個進程引起的Crash獲取其餘異常不會致使整個瀏覽器崩潰

Chromium的四類主要進程:

1. Browser進程

負責合成瀏覽器的UI,包括標題欄、地址欄、工具欄以及各個TAB的網頁內容。一個Chromium實例只有一個Browser進程。

2. Render進程

負責解析和渲染網頁的內容。通常來講,一個TAB就對應有一個Render進程。

咱們也能夠設置啓動參數,讓具備相同的域名的TAB都運行在同一個Render進程中。

不管是Browser進程,仍是Render進程,當啓用了硬件加速渲染時,它們都是經過GPU進程來渲染UI的。不過Render進程是將網頁內容渲染在一個離屏窗口的,例如渲染在一個Frame Buffer Object上,而Browser進程是直接將UI渲染在Frame Buffer上,也就是屏幕上。

正由於如此,Render進程渲染好的網頁UI要通過Browser進程合成以後,才能在屏幕上看到。

(題外話,提及離屏窗口,想到了以前項目中的一個作法:在一個bindows項目中,拖拽窗體時,將窗體內的元素扔到視口外,待拖拽中止後,再將它們放回來)

3. Plugin進程

用來運行第三方開發的Plugin,以便擴展瀏覽器的功能。Flash就是一個Plugin,它運行在獨立的Plugin進程中。

爲了不建立過多的Plugin進程,同一個Plugin的不一樣實例都是運行在同一個Plugin進程中的。

也就是說,無論是在同一個TAB的網頁建立的同類Plugin,仍是在不一樣TAB的網頁建立的同類Plugin,它們都是運行在同一個Plugin進程中。

4. GPU進程

GPU進程在前面介紹渲染時介紹過了,這裏再也不贅述。


在Chromium的官網文檔中,貌似沒有找到關於省電節能的內容。從網上查到的內容來看,Chromium彷佛也是比較費電的,聽說由於沙盒的限制,當Chromium播放視頻時,只能使用GPU加速渲染,而不能加速解碼。不過這塊我並不瞭解,不作展開。

安全

當咱們經過瀏覽器訪問網頁的時候,瀏覽器須要保護咱們數據的安全。像cookie,帳號/密碼這類涉及我的隱私,交易支付的信息,不能被惡意網頁獲取。

同源策略

瀏覽器的安全模型中,域是十分重要的概念,域由協議+域名+端口構成。不一樣域之間的資源訪問受到嚴格控制。頁面的DOM用戶數據XHR都沒法跨域訪問。

CSP

實際狀況中,同源策略對於不少XSS(跨站腳本攻擊)無能爲力,由於很多資源是能夠跨域加載的(image,JS)。瀏覽器提供了CSP來防止XSS,使用HTTP消息頭來指定網頁容許哪些域中的哪些資源能夠被加載。

CSP經過HTTP頭部由服務端制定,頭部類型因爲歷史緣由總共由三種,這三種僅僅是兼容性的差異,針對Chromium瀏覽器,咱們僅需關注Content-Security-Policy頭部。CSP頭部的定義規則以下:

Content-Security-Policy: 名 值; 名 值; 名 值;


指令值的規範以下圖:

HTTPS

這個你們都比較熟悉了,不作專門介紹。

沙箱模型

前面介紹過,Chromium是多進程架構,網頁的渲染是在獨立的Render進程中進行,這爲實現沙箱模型提供了基礎,將網頁渲染放在一個權限受限的進程中進行。

沙箱模型依賴操做系統提供的技術,不一樣操做系統提供的安全技術不同,因此沙箱模型在不一樣系統上的實現也是有差異的。

以Chromium在Android中的沙箱模型爲例。據資料,Android的沙箱和Linux下是同樣的。


Site Isolation

Site Isolation (網站隔離) 是 Chromium 爲應對潛在的安全問題所實現的功能,以防止惡意網站獲取其餘網站的信息。

Site isolation 提供了同源策略以外的第二層的額外保護,將同源策略與進程的地址空間隔離結合起來把不一樣的網站隔離在不一樣的進程中,而且阻止一個進程得到其餘網站的敏感信息。這樣即便存在 spectre 類型的旁路攻擊,能夠獲取進程內任意內存地址的數據,也不能得到其餘網站的信息。

Site Isolation主要由兩部分組成。進程模型的修改和跨域讀取屏蔽 (CORB)。

site-per-process

目前 Chromium 默認的進程模型叫作 process-per-site-instance (還有其餘的進程模型如 process-per-site 和 process-per-tab)[3]。這個進程模型基本上就是爲每一個頁面建立一個進程,可是仍是存在不一樣的網站用同一個進程的狀況,如 iframes 和父頁面同一個標籤頁裏的頁面跳轉以及標籤頁過多的時候等。Site isolation 引入了一個新的策略叫作 site-per-process。這個策略更爲嚴格,只要是不一樣的網站,無論你是在新的標籤頁打開,仍是在同一個標籤頁跳轉,仍是嵌在 iframes 裏,通通都要換一個新的進程。這裏主要的工做量是把 iframes 給拿出來放到不一樣的進程裏(所謂的 OOPIF, out of process iframe)。

使用同一個協議,同一個註冊域名 (所謂的 eTLD+1) 的網址都屬於同一個網站,這比同源策略裏的 same origin 要寬泛一些,不一樣的子域名,不一樣的端口都算同一個網站。

CORB

CORB (Cross-Origin Read Blocking) 是一個屏蔽跨域資源加載的功能。

同源策略能夠防止惡意網站獲取其餘網站的信息,但有一些例外如 <img>和<script>。相似<img src="https://example.com/secret.json">的跨站請求能夠發起,只是返回的結果被過濾掉了,在解析圖片時出錯[5]。這時候跨域的資源其實已經傳入到了這個進程裏面,結合 spectre 類型的旁路攻擊或者其餘漏洞是能夠拿到這些信息的。 CORB 的想法就是直接屏蔽掉跨域資源返回的結果,讓地址空間裏都沒有返回的結果。目前只有HTML,XML 和 JSON 類型的資源會被 CORB 保護

參考資料

《Webkit技術內幕》

Chrome背後的故事(系列)

從Chrome源碼看瀏覽器如何加載資源

理解WebKit和Chromium: WebKit資源加載機制

HTML5 Canvas,WebGL,CSS Shaders,GLSL的曖昧關係

活用 Shader,讓你的頁面更小,更炫,更快

瀏覽器頁面資源加載過程與優化

緩存淘汰算法--LRU算法

Chrome中的高性能網絡(二)

Chrome 渲染管道的性能改進

HTTP緩存——304與200 from cache

圖解瀏覽器的基本工做原理

從瀏覽器多進程到JS單線程,JS運行機制最全面的一次梳理

21天自制chromium之渲染篇(1)

chromium cc層的一處性能優化點

Inside look at modern web browser (part 4)

現代瀏覽器內部工做原理

Chromium網頁渲染調度器(Scheduler)實現分析

Chromium網頁渲染機制簡要介紹和學習計劃

Chromium網頁加載過程簡要介紹和學習計劃

Multi-process Architecture

IE 安全系列:IE 瀏覽器的技術變遷(上)

IE安全系列:IE瀏覽器的技術變遷(下)

在新窗口中打開頁面?當心有坑!

沙箱

Chromium多線程模型設計和實現分析

Chromium多進程架構簡要介紹和學習計劃

【OpenGL】17-幀緩衝與離屏渲染

Chrome 的 GPU 進程的背後

The whole web at maximum FPS: How WebRender gets rid of jank

Chrome Site Isolation 簡介

渲染性能

XSS分析及預防

Why the New V8 is so Damn Fast

滾動優化(無限滾動加載、滾動元素內有大量dom,形成卡頓問題的優化方案)

相關文章
相關標籤/搜索