從“快穩省安全”看Chromium——Chromium學習系列

發客發表於2019-03-06

前言

對於不同的角色,瀏覽器有著不同的意義:

普通使用者通過它來瀏覽網頁,在乎的是穩定,高效和安全;

前端開發同學關心的是相容性和效能優化。我們可以通過學習Chromium的內部機制,更好的理解各種優化手段。

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

載入快

Chromium拿到頁面請求後,首先對需要請求的資源做分類,然後根據相關的安全策略,對資源做許可權校驗,接著對載入順序按優先順序排序,再根據順序進行載入。

Chromium支援的資源分為如下14類:

型別

介紹

kMainResource

即主資源,html頁面檔案資源就屬於該型別,包括location即導航輸入地址得到的頁面、使用frame/iframe巢狀的、通過超連結點選的頁面以及表單提交這幾種

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對資源的優先順序是這樣定義的:

從“快穩省安全”看Chromium——Chromium學習系列從“快穩省安全”看Chromium——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——Chromium學習系列從“快穩省安全”看Chromium——Chromium學習系列

多程式資源載入

Chromium採用的是多程式資源載入的機制:

從“快穩省安全”看Chromium——Chromium學習系列

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

這種架構的一個優點是,所有的資源請求都完全在I/O執行緒上處理,使用者介面產生的活動與網路事件之間互不干擾。資源過濾器執行在Browser程式中的I/O執行緒中,截獲資源請求訊息,並將其轉發給Browser程式中的ResourceDispatcherHost單例。

這個單例介面允許瀏覽器控制各個Render程式的網路訪問許可權,它還能實現高效和一致性的資源共享,例如:

1. socket池和連線限制:瀏覽器能夠對每個profile、代理和{scheme, host, port}組所對應的已開啟socket數量進行限制(分別為256、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(增壓渦輪)。

這樣做的目的一是降低記憶體佔用,二是減少啟動時間,三是降低複雜度。

從“快穩省安全”看Chromium——Chromium學習系列從“快穩省安全”看Chromium——Chromium學習系列


Composite

Composite是一種技術,Chromium會把頁面分成多層,開啟多個Compositor執行緒,分別對它們進行光柵化。當頁面有滾動發生時,因為各層已經做了光柵化,所以要做的就是合成新的幀。

為了找出哪些元素屬於哪一層,主執行緒會根據Layout Tree生成Layer Tree。如果你希望某個元素分層渲染,可以通過設定will-change這個樣式屬性,提醒瀏覽器這麼做。

從“快穩省安全”看Chromium——Chromium學習系列

一旦建立了層樹並確定了繪製順序,主執行緒就會將該資訊提交給compositor(合成器)執行緒。 compositor執行緒隨後柵格化各層。 一個圖層可能像頁面的整個長度一樣大,因此合成器執行緒將它們分成多個圖塊並將每個圖塊傳送到光柵執行緒。 柵格執行緒柵格化每個圖塊並將它們儲存在GPU記憶體中。

從“快穩省安全”看Chromium——Chromium學習系列

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 這些非連續事件則會立即被觸發。

從“快穩省安全”看Chromium——Chromium學習系列

從“快穩省安全”看Chromium——Chromium學習系列

多程式模型

之前介紹多程式資源載入的時候,簡單提到過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的官網文件中,貌似沒有找到關於省電節能的內容。從網上查到的內容來看,Chromium似乎也是比較費電的,據說因為沙盒的限制,當Chromium播放視訊時,只能使用GPU加速渲染,而不能加速解碼。不過這塊我並不瞭解,不做展開。

安全

當我們通過瀏覽器訪問網頁的時候,瀏覽器需要保護我們資料的安全。像cookie,賬號/密碼這類涉及個人隱私,交易支付的資訊,不能被惡意網頁獲取。

同源策略

瀏覽器的安全模型中,域是十分重要的概念,域由協議+域名+埠構成。不同域之間的資源訪問受到嚴格控制。頁面的DOM使用者資料XHR都無法跨域訪問。

CSP

實際情況中,同源策略對於很多XSS(跨站指令碼攻擊)無能為力,因為不少資源是可以跨域載入的(image,JS)。瀏覽器提供了CSP來防止XSS,使用HTTP訊息頭來指定網頁允許哪些域中的哪些資源可以被載入。

CSP通過HTTP頭部由服務端制定,頭部型別由於歷史原因總共由三種,這三種僅僅是相容性的差別,針對Chromium瀏覽器,我們僅需關注Content-Security-Policy頭部。CSP頭部的定義規則如下:

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

從“快穩省安全”看Chromium——Chromium學習系列從“快穩省安全”看Chromium——Chromium學習系列


指令值的規範如下圖:

從“快穩省安全”看Chromium——Chromium學習系列從“快穩省安全”看Chromium——Chromium學習系列

HTTPS

這個大家都比較熟悉了,不做專門介紹。

沙箱模型

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

沙箱模型依賴作業系統提供的技術,不同作業系統提供的安全技術不一樣,所以沙箱模型在不同系統上的實現也是有差別的。

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

從“快穩省安全”看Chromium——Chromium學習系列從“快穩省安全”看Chromium——Chromium學習系列


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,造成卡頓問題的優化方案)


相關文章