從進(jìn)程和線程了解瀏覽器的工作原理

進(jìn)程和線程

進(jìn)程(process)和線程(thread)是操作系統(tǒng)的基本概念。

現(xiàn)代操作系統(tǒng)都是可以同時運(yùn)行多個任務(wù)的,比如:用瀏覽器上網(wǎng)的同時還可以聽音樂。對于操作系統(tǒng)來說,一個任務(wù)就是一個進(jìn)程,比如打開一個瀏覽器就是啟動了一個瀏覽器進(jìn)程,打開一個 Word 就啟動了一個 Word 進(jìn)程。

有些進(jìn)程同時不止做一件事,比如 Word,它同時可以進(jìn)行打字、拼寫檢查、打印等事情。在一個進(jìn)程內(nèi)部,要同時做多件事,就需要同時運(yùn)行多個“子任務(wù)”,我們把進(jìn)程內(nèi)的這些“子任務(wù)”稱為線程。

由于每個進(jìn)程至少要做一件事,所以一個進(jìn)程至少有一個線程。

系統(tǒng)會給每個進(jìn)程分配獨立的內(nèi)存,因此進(jìn)程有它獨立的資源。同一進(jìn)程內(nèi)的各個線程之間共享該進(jìn)程的內(nèi)存空間(包括代碼段,數(shù)據(jù)集,堆等)。

如果電腦是 windows 系統(tǒng),打開任務(wù)管理器,可以看到有一個后臺進(jìn)程列表,在這里我們可以看到每個進(jìn)程的內(nèi)存資源信息以及 CPU 占有率。

我們再用官方的術(shù)語描述一下:

進(jìn)程是 CPU 資源分配的最小單位(是能擁有資源和獨立運(yùn)行的最小單位)。

線程是 CPU 調(diào)度的最小單位(是建立在進(jìn)程基礎(chǔ)上的一次程序運(yùn)行單位)。

瀏覽器是多進(jìn)程的

理解了進(jìn)程和線程之后,接下來我們對瀏覽器進(jìn)行一定程度上的認(rèn)識。

瀏覽器是多進(jìn)程的,每打開一個 tab 頁,就相當(dāng)于創(chuàng)建了一個獨立的瀏覽器進(jìn)程。

圖中打開了 Chrome 瀏覽器的多個 tab 頁,在 Chrome 任務(wù)管理器中可以看到有多個進(jìn)程,每一個 tab 頁有一個獨立的進(jìn)程。

注意:瀏覽器應(yīng)該也有自己的優(yōu)化機(jī)制,有時候打開多個 tab 頁,在 Chrome 任務(wù)管理器中會看到有些進(jìn)程被合并了,所以每個 tab 頁對應(yīng)一個進(jìn)程并不一定是絕對的。

瀏覽器包含哪些進(jìn)程?

為了簡化理解,這里僅列舉主要進(jìn)程。

  • Browser 進(jìn)程:瀏覽器的主進(jìn)程,只有一個。

    • 負(fù)責(zé)瀏覽器界面的顯示與交互;

    • 各個頁面的管理,創(chuàng)建和銷毀其他進(jìn)程;

    • 網(wǎng)絡(luò)的資源管理、下載等。

  • Renderer 進(jìn)程:也稱為瀏覽器渲染進(jìn)程瀏覽器內(nèi)核,內(nèi)部是多線程的。主要負(fù)責(zé)頁面渲染,腳本執(zhí)行,事件處理等。

  • 第三方插件進(jìn)程:每種類型的插件對應(yīng)一個進(jìn)程,僅當(dāng)使用該插件時才創(chuàng)建。

  • GPU 進(jìn)程:最多一個,用于 3D 繪制等。

瀏覽器多進(jìn)程的優(yōu)勢

  • 由于默認(rèn) 新開 一個 tab 頁面 新建 一個進(jìn)程,所以單個 tab 頁面崩潰不會影響到整個瀏覽器;

  • 同樣,第三方插件崩潰也不會影響到整個瀏覽器;

  • 多進(jìn)程可以充分利用現(xiàn)代 CPU 多核的優(yōu)勢;

  • 方便使用沙盒模型隔離插件等進(jìn)程,提高瀏覽器的穩(wěn)定性。

系統(tǒng)為瀏覽器新開的進(jìn)程分配內(nèi)存、CPU 等資源,所以內(nèi)存和 CPU 的資源消耗也會更大。

瀏覽器內(nèi)核(渲染進(jìn)程)

前面說了這么多的進(jìn)程,對普通前端操作來說,最重要的還是渲染進(jìn)程。

瀏覽器的渲染進(jìn)程是多線程的,頁面的渲染,JS的執(zhí)行,事件的循環(huán)等,都在這個進(jìn)程內(nèi)執(zhí)行。

渲染進(jìn)程通常由以下常駐線程組成:

1. GUI 渲染線程

負(fù)責(zé)渲染瀏覽器界面,解析 HTML、CSS,構(gòu)建 DOM tree和 render tree,布局和繪制等。當(dāng)界面需要重繪(repaint)或由于某種操作引發(fā)回流(reflow)時,該線程就會執(zhí)行。

2. JS 引擎線程

也稱為 JS 內(nèi)核,負(fù)責(zé)解析 JavaScript 腳本,運(yùn)行代碼。

  • JavaScript 是單線程的。

    JavaScript 為什么是單線程的?這與它的用途有關(guān)。JavaScript 作為瀏覽器腳本語言,主要用途是與用戶互動以及操作 DOM。這也決定了它只能是單線程的,否則會帶來很復(fù)雜的同步問題。想想一下,如果 JavaScript 同時有連個線程,一個線程在某個 DOM 節(jié)點上添加內(nèi)容,另一個線程刪除了這個 DOM 節(jié)點,這時瀏覽器應(yīng)該以哪個線程為準(zhǔn)呢?所以,為了避免復(fù)雜性,JavaScript 從一開始就是單線程。

  • GUI 渲染線程 與 JS 引擎線程是互斥的

    由于 JavaScript 可以操作 DOM,如果在修改元素屬性的同時渲染界面(即 JavaScript 引擎線程和 GUI 渲染線程同時運(yùn)行),那么渲染線程前后獲得的元素數(shù)據(jù)就可能會不一致。因此,為了防止渲染出現(xiàn)不可預(yù)期的結(jié)果,瀏覽器設(shè)置 GUI 渲染線程與 JS 引擎為互斥的關(guān)系。當(dāng) JS 引擎執(zhí)行時,GUI 線程被掛起,GUI 更新被保存在一個隊列中,等到 JS 引擎線程空閑時立即被執(zhí)行。

  • JS 阻塞頁面加載。

    由于 GUI 渲染線程與 JS 引擎線程是互斥的,當(dāng)瀏覽器在執(zhí)行 JavaScript 的時候,GUI 渲染線程會被保存在一個隊列中,直到 JS 程序執(zhí)行完成,才會接著執(zhí)行。因此如果 JS 執(zhí)行時間過長,就會造成頁面的渲染不連貫,導(dǎo)致頁面渲染加載阻塞。

3. 事件觸發(fā)線程

當(dāng)一個事件被觸發(fā)時,該線程會把事件添加到待處理隊列的隊尾,等待 JS 引擎處理。這些事件可以是當(dāng)前執(zhí)行的代碼塊,如定時任務(wù);也可以是來自瀏覽器內(nèi)核的其他線程,如:鼠標(biāo)點擊、Ajax異步請求等。但由于 JS 是單線程的,這些事件都需要排隊等待 JS 引擎處理。

4. 定時觸發(fā)器線程

setTimeoutsetInterval 所在的線程。瀏覽器定時計數(shù)器并不是由 JS 引擎計數(shù)的,因為 JS 是單線程的,如果處于阻塞線程狀態(tài)就會影響計時的準(zhǔn)確,所以通過單獨的線程來計時并觸發(fā)定時更為合理。

5. 異步 http 請求線程

XMLHttpRequest 在建立連接后,通過瀏覽器新開一個線程請求,一旦檢測到狀態(tài)變更并且設(shè)置有回調(diào)函數(shù),異步線程就產(chǎn)生狀態(tài)變更事件,將這個回調(diào)再放入事件隊列中,等待 JS 引擎空閑時處理。

Browser 進(jìn)程和 Renderer 進(jìn)程的通信過程

打開瀏覽器的一個 tab 頁時,我們看下其中的大致過程:

  • Browser 進(jìn)程收到用戶請求,通過網(wǎng)絡(luò)下載獲取頁面內(nèi)容,然后將該任務(wù)通過RendererHost接口傳遞給 Renderer 進(jìn)程;

  • Renderer 進(jìn)程的 Renderer 接口收到消息,簡單解釋后,交給 GUI 渲染線程開始渲染;

    • GUI 渲染線程接收請求,加載網(wǎng)頁并渲染網(wǎng)頁,這個過程中可能需要 Browser 進(jìn)程獲取資源和 GPU 進(jìn)程來幫助渲染,也可能會有 JS 引擎線程操作 DOM(可能造成回流并重繪);

    • 最后 Renderer 進(jìn)程將結(jié)果傳遞給 Browser 進(jìn)程;

  • Browser 進(jìn)程接收到結(jié)果,并將結(jié)果繪制出來。

到這里應(yīng)該對瀏覽器的運(yùn)作有一定理解了,我們再來看下瀏覽器是怎么渲染頁面的。

瀏覽器的渲染流程

瀏覽器內(nèi)核拿到頁面內(nèi)容后,渲染過程大概分為以下幾個部分:

  1. 解析 HTML 文件,生成 DOM tree;同時解析 CSS 文件以及樣式元素中的樣式數(shù)據(jù),生成 CSS Rules。
  2. 構(gòu)建 render tree:根據(jù) DOM tree 和 CSS Rules 來構(gòu)建 render tree,它可以讓瀏覽器按照正確的順序繪制內(nèi)容。
  3. 布局(layout / reflow):計算各元素尺寸、位置。
  4. 繪制(paint):繪制頁面像素信息。
  5. 瀏覽器將各層信息發(fā)送給 GPU,GPU 將各層信息合成(composite),顯示在屏幕上。

補(bǔ)充:
Webkit 將 render tree 中的元素稱為 render object (或 renderer),每一個 render object 都代表一個的矩形區(qū)域,通常對應(yīng)于相關(guān)節(jié)點的 CSS 框,這些矩形的排列順序就是它們在屏幕上顯示的順序。

Render object 和 DOM 節(jié)點是相對應(yīng)的,但并非一一對應(yīng)。非可視化的 DOM 元素不會插入 render tree 中,例如“head”元素 和 一些 display: none 的節(jié)點就沒必要放在 render tree 中了。

這里只是大致的過程,詳細(xì)步驟可以看參考資料中的第一篇。
渲染完成后,接下來就是 JavaScript 邏輯處理了。

參考資料

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容