您當前的位置:首頁 > 書法

深入淺出瀏覽器架構

作者:由 blackgan 發表于 書法時間:2019-10-16

瀏覽器是我們上網的一個重要工具,是我們重要的資訊來源,這裡以Chrome瀏覽器為物件,同時作為一名前端工程師,之前對於瀏覽器的認知還不夠深入,所以藉著這一系列的文章,進行瀏覽器的逐步分析與學習,加深自己的知識儲備。同時這部分知識也是做頁面效能最佳化,健康度監控等工具時所必須的基礎知識。

多程序多執行緒的瀏覽器

首先我們先看下什麼是程序和執行緒

深入淺出瀏覽器架構

程序是系統記憶體分配的一小部分記憶體空間

程序之間相互獨立(不同程序之間可以相互通訊(IPC),不過代價很大)

程序由單個或多個執行緒組成

多個執行緒之間是可以相互協作完成工作的

同一個程序中各個執行緒之間共享同一塊記憶體空間

瀏覽器的多程序

chrome瀏覽器使用的是多程序多執行緒模式,因為現在的網頁複雜性非常高。如果整個瀏覽器是單程序的,有可能某個page介面的拋錯就會導致整個瀏覽器的crash。同時多個介面互相可以訪問相同的記憶體和相同的執行環境,安全性也是一個大的問題,所以瀏覽器需要採用多程序模式。

深入淺出瀏覽器架構

從圖中可以看到,有一個網路程序,一個瀏覽器主程序,一個GPU程序,多個渲染程序和多個外掛程序

1,瀏覽器主程序(Browser程序):控制chrome的位址列,書籤欄,返回和前進按鈕,同時提供儲存功能

2,第三方外掛程序:每種外掛一個程序,外掛執行時才會建立

3,瀏覽器渲染程序(瀏覽器核心,內部是多執行緒的):負責介面渲染,指令碼執行,事件處理等

4,GPU程序:用於3D繪製

5, 網路程序:主要負責頁面的網路資源載入,之前是作為一個模組執行在瀏覽器程序裡邊的,最近獨立為了一個程序

同時,chrome團隊也在進行瀏覽器架構更改,提出了面向服務的架構。將瀏覽器程式的每個模組作為一項服務執行,每個服務都可以在獨立的程序中執行,訪問服務必須使用定義好的介面,透過IPC來通訊,從而構建一個更內聚,松耦合,易於維護和擴充套件的系統,更好實現Chrome簡單,穩定,高速,安全的目標。具體的架構表現圖為

深入淺出瀏覽器架構

但是在資源被約束的裝置上,Chrome會將很多服務整合到一個程序中,從而節省記憶體佔用。

深入淺出瀏覽器架構

多程序協作渲染介面

在上一part 中,我們已經瞭解了瀏覽器是多程序架構的,以及各個架構的作用,同時也學習了Chrome瀏覽器在資源充足的機器上準備使用的面向服務架構。瀏覽器的主要任務是展示介面給使用者看,讓使用者操作網頁,在這一part,我們會透過在地址框輸入url -> 介面展示 這整個過程來看一下瀏覽器多個程序之間的協作。

首先,使用者從瀏覽器程序裡輸入請求資訊

然後,網路程序發起URL請求;

伺服器響應URL請求以後,瀏覽器程序就又要開始準備渲染程序了;

渲染程序準備好之後,需要先向渲染程序提交頁面資料,也就是提交文件階段;

渲染程序接受完文件資訊之後,便開始解析頁面和載入子資源,完成頁面的渲染;

深入淺出瀏覽器架構

瀏覽器導航過程

在tab網頁之外的所有功能,都是由瀏覽器程序控制的,瀏覽器程序中有繪製瀏覽器的按鈕和輸入框的UI執行緒,網路執行緒來處理網路堆疊從網路中獲取資料,儲存執行緒控制檔案的許可權等。當輸入url按下enter鍵後,UI執行緒會處理我們的輸入。

深入淺出瀏覽器架構

1,處理位址列中的輸入

使用者在位址列輸入之後,瀏覽器程序中的UI執行緒回去判斷使用者的輸入是url還是一個搜尋項,因為chrome的位址列也可以作為一個搜尋輸入框來使用。所以UI執行緒需要判斷使用者的的輸入然後決定把使用者的輸入傳送給搜尋引擎還是發起網路請求。

深入淺出瀏覽器架構

2,URL請求過程

接下來,就是頁面資源請求過程,瀏覽器程序會透過IPC把URL請求傳送至網路程序,網路程序接收到URL請求後,會發起真正的URL請求。

首先網路程序會查詢本地快取是否有該資源,有就直接返回,沒有就進入請求流程。會先經過DNS解析-》建立TCP連結-》接收返回資訊-》解析響應頭

其中服務端有可能給網路程序返回一個重定向的狀態例如HTTP301,網路程序會從響應頭的Location欄位裡邊讀取重定向的地址。

3,響應資料處理

在網路請求完成之後,伺服器返回資料給瀏覽器,但是伺服器返回的型別有很多種,瀏覽器會根據響應頭的Content-Type來判斷返回值的型別,如果服務端返回的是資料是HTML,網路執行緒會把資料直接傳送給渲染程序(瀏覽器核心),但是如果是zip檔案或者是其他型別的檔案,這代表著這是一次下載請求,網路執行緒會把資料傳送給下載管理器。

深入淺出瀏覽器架構

同時這個過程也會發生瀏覽器的安全檢查,如果響應域和返回資料與已知的惡意站點所匹配,網路執行緒會告訴渲染程序展示一個警告介面,同時為了確保不會把危險的跨域資料傳送給渲染程序也會進行CORB檢測。

4,準備渲染程序

當所有的檢查都完成後,網路程序確定瀏覽器應該導航到對應的請求站點,網路程序告訴瀏覽器程序資料已經準備完畢,然後瀏覽器程序便開始去準備渲染程序。

不同源的介面,會有一個單獨的渲染程序,如果是從同源的A介面開啟B介面,則B介面的渲染程序會複用A介面的渲染程序。

由於網路請求需要幾百毫秒才能得到響應返回,瀏覽器應用了一種用來加速這個過程的最佳化策略,當瀏覽器程序傳送一個網路請求給網路程序的同時, 瀏覽器程序會主動嘗試去查詢或啟動一個渲染程序,這個過程是並行執行的。使用這種方法,如果一切進展順利的話,當網路程序接收到資料時,渲染程序已經處於待機狀態了,當然如果請求被重定向的話則不會用到這個準備好的渲染程序。

渲染程序準備好之後,還不能立即進入文件解析狀態,因為此時的文件資料還在網路程序中,並沒有提交給渲染程序,所以下一步就進入了提交文件階段。

5,提交文件

這裡的“文件”是指URL請求的響應題資料

“提交文件”的訊息是由瀏覽器程序發出的,渲染程序接收到“提交文件”的訊息後,會和網路程序建立傳輸資料的“管道”。

文件資料傳輸完成之後,渲染程序會返回“確認提交”的訊息給瀏覽器程序。

瀏覽器程序在接收到“確認提交”的訊息後,會更新瀏覽器介面狀態,包括安全狀態,位址列的URL,前進後退的歷史狀態,並更新Web介面。

到此,瀏覽器的導航流程完成了,導航流程很重要,它是網路載入流程和渲染流程之間的一座橋樑,這個過程涵蓋了從使用者發起請求到提交文件給渲染程序中間的所有階段。

瀏覽器渲染過程

————————————————-

渲染程序控制頁面內容

瀏覽器程序負責tab頁中發生的所有事情,在渲染程序中,主執行緒處理大部分的程式碼,同時如果使用了web worker或者service worker,則工作執行緒也會處理一部分程式碼。合成器和光柵執行緒也執行在渲染程序中來高效,流暢的渲染介面。

渲染器程序的主要工作是將HTML,CSS和JavaScript轉換為使用者可以與之互動的網頁。

深入淺出瀏覽器架構

構造DOM

當渲染程序接收到導航過程中瀏覽器程序的提交文件資訊並開始接收HTML資料時,主執行緒開始解析HTML並將其轉換為DOM。

DOM是能夠被瀏覽器理解的結構,並且透過JavaScript可以進行修改。

深入淺出瀏覽器架構

當HTML解析器遇到了script標籤,它會暫停HTML文件的解析,並且去載入-》解析-》執行js程式碼,因為js中可能存在類似於document。write這樣的改變DOM結構的語句。

樣式計算

樣式計算的目的是為了計算出DOM節點中每個元素的具體樣式,這個階段大體分為三步來完成。

1,把CSS轉換為瀏覽器能夠理解的結構,和HTML檔案一樣,瀏覽器也是無法直接理解這些純文字的CSS樣式,所以當渲染引擎接收到CSS文字,會將CSS文字轉換為瀏覽器可以理解的結構——-styleSheets。並且該結構同時具備了查詢和修改功能,也就是後續的樣式操作。

2,轉換樣式表中的屬性值,使其標準化,也就是對styleSheets的屬性值進行標準化操作。

深入淺出瀏覽器架構

3,計算出DOM樹中每個節點的具體樣式,透過CSS的繼承規則和層疊規則,最終會輸入每個DOM節點的樣式,並被儲存在ComputedStyle的結構內。

佈局階段

有了DOM樹和DOM樹上的元素的樣式後,我們還不知道DOM樹上的元素在螢幕上的具體位置,接下來就是要計算DOM樹中可見元素的幾何位置,這個過程就是佈局。

1,建立佈局樹

DOM樹中display:none的元素是不可見的,所以需要根據DOM樹和樣式計算建立一個可見元素佈局樹。

深入淺出瀏覽器架構

分層

在得到了DOM樹, DOM樹上元素的樣式,每個元素在頁面上的幾何位置之後,還不能直接渲染出我們想要的介面,我們還需要知道渲染介面時元素的渲染位置。

例如當某個元素上有 z-index屬性時,就不能按照元素原有的順序進行繪製,否則就會出錯,見下述例子:

深入淺出瀏覽器架構

在渲染介面之前,我們需要對元素進行分層,主執行緒會遍歷佈局樹進行圖層的劃分,如果某個元素沒有得到其該有的單獨的圖層,可以給它新增will-change屬性來提醒瀏覽器。

合成

現在渲染程序知道了DOM結構,每個元素的樣式,元素在頁面上的幾何位置,元素的渲染先後。渲染程序將這些資訊轉換為螢幕上的畫素的過程被叫做柵格化。

一種最簡單原始的做法就是 只對使用者瀏覽器當前視覺化的部分做柵格化,當用戶滾動頁面時,再透過柵格化去填補缺失的部分。但是目前現代瀏覽器執行著一個更復雜的過程,就是合成。

合成是一種將頁面的各個部分進行分層的技術,分別對不同的圖層進行柵格化,並在合成執行緒中作為頁面進行合成。因為圖層都已經被柵格化,在發生滾動的時候,只需要進行圖層的移動來合成一個新的幀就行了。

深入淺出瀏覽器架構

從上圖可知,當圖層的繪製列表準備好之後,主執行緒會把該繪製列表提交給合成執行緒,因為有些圖層可能會很大,比如有些介面需要滾動好久才能滾動到底部,這時候,合成執行緒就會將圖層劃分為圖塊來優先滿足 瀏覽器可視區域的渲染,這些圖塊的大小通常是256X256或者 512X512, 然後合成執行緒會按照 可視區域附近的圖塊來優先生成點陣圖,這裡生成點陣圖的操作就是柵格化。

同時渲染程序維護了一個柵格化的執行緒池,所有圖塊柵格化都是線上程池中執行的:

深入淺出瀏覽器架構

柵格化的過程會使用GPU來加速生成,使用GPU生成點陣圖的過程叫做快速柵格化,或者GPU柵格化,生成的點陣圖都被儲存在GPU記憶體中。

一旦所有的圖塊都被柵格化,合成執行緒就會生成一個繪製圖塊的命令——“Draw Quad”, 然後將該命令提交給瀏覽器程序。

瀏覽器程序接收到命令後,會將頁面內容繪製到記憶體中,最後再將記憶體顯示在螢幕上。

深入淺出瀏覽器架構

總結

到這裡,瀏覽器的整體程序互動和渲染過程就結束了。總結下來就是:

渲染程序將HTML內容轉換為DOM樹

渲染引擎將CSS樣式轉換為styleSheets,計算出DOM節點的樣式;

建立佈局樹,並計算出佈局樹的佈局資訊(幾何資訊);

對佈局樹進行分層,生成分層樹;

每個圖層生成繪製列表,提交至合成執行緒;

合成執行緒將圖層分為圖塊,並在光柵化執行緒池中將圖塊轉換為點陣圖, 中間可能伴隨有GPU加速;

合成執行緒傳送 DrawQuad給瀏覽器程序。

瀏覽器程序根據命令訊息生成頁面,顯示到顯示器上。

重排:

當使用js 改變某個元素的大小,寬高,會引起重新計算佈局,分層,繪製,合成等步驟。

重繪:

當使用js 改變某個元素的顏色,會引起 重新繪製,光柵化,合成等步驟,節省了佈局和分層階段,相較於重排執行效率會高一些。

直接合成:

使用cssS 的動畫效果,可以避免排列和繪製階段,直接在合成執行緒或光柵化執行緒池中進行操作,因為並沒有佔用主執行緒,所以直接合成的效率是最高的。

引用:

極客時間:瀏覽器工作原理與實踐

https://

developers。google。com/w

eb/updates/2018/09/inside-browser-part1

https://

developers。google。com/w

eb/updates/2018/09/inside-browser-part2

https://

developers。google。com/w

eb/updates/2018/09/inside-browser-part3

標簽: 瀏覽器  程序  渲染  執行緒  dom