整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          圖解瀏覽器的多進程渲染機制

          者簡介:高揚,來自抖音社區安全前端團隊,團隊負責的工作重點在于降低社區中不良內容與行為對用戶造成的傷害。

          引言

          觀察瀏覽器的任務管理器可以發現,打開瀏覽器的一個頁面需要多個進程,包括瀏覽器進程、GPU進程、網絡進程、渲染進程等,有插件的話還會包括各種插件進程(Chrome選項 -> 更多工具 -> 任務管理器)。

          本文將聚焦于瀏覽器的各個進程間是如何配合,將頁面呈現給用戶的。

          你將了解到

          1. 瀏覽器在歷史發展過程中,其進程架構做了哪些調整,為什么這樣調整,以及解決了哪些問題?
          2. 從用戶在地址欄輸入URL,到頁面渲染完成這之間發生了什么?回流和重繪是如何對瀏覽器性能造成影響的?

          1.瀏覽器進程架構的演化

          進程和線程

          1. 進程
            • 一個進程就是一個程序的運行實例,它是由用來存放代碼、運行中的數據以及一個執行任務的主線程的內存組成的運行環境;
            • 當一個進程關閉后,操作系統會回收為該進程分配的內存(即使該進程中存在因操作不當導致內存泄漏的線程);
            • 進程之間的內容是相互隔離的,這是為了保護操作系統中的進程互不干擾;
            • 當進程之間需要進行通信時,可使用進程間通信(IPC)機制。
          1. 線程
            • 線程是由進程來啟動和管理的,一個應用程序在執行的時候會存在多個子任務的情況,使用多線程并行處理可以大大提升性能;
            • 由于線程依附于進程,進程中的任一線程執行出錯也會導致整個進程的崩潰(因為內存是共享的);
            • 同一進程中的多個線程可共享進程所擁有的資源。這種資源包括內存空間,也包括操作系統的權限。

          單進程和多進程瀏覽器

          單進程瀏覽器

          單進程瀏覽器是指所有功能模塊(網絡、插件、JS運行環境、渲染引擎、頁面等)都運行在同一進程中的瀏覽器(早期的IE、Firefox)。

          單進程瀏覽器存在的問題:

          • 【不穩定】
            • 瀏覽器中的插件運行在瀏覽器的進程之中,插件的崩潰會引起整個瀏覽器的崩潰;
            • 渲染引擎通常也是不穩定的,例如復雜的JS腳本也會引起渲染引擎的崩潰,最終導致瀏覽器崩潰。
          • 【不流暢】
            • CPU在某個時間點只能執行某個進程中的某一條線程。由于單進程瀏覽器中所有的頁面的各種模塊都在同一線程中運行,即同一時刻只能有一個模塊可以執行。
            • 當一個頁面的某個模塊阻塞了該線程,就會導致整個瀏覽器失去響應;此外,頁面的內存泄漏也會導致單進程瀏覽器使用時間越長,反應越慢。
          • 【不安全】
            • 線程共享進程資源,因而插件就能獲取到瀏覽器運行過程中的數據,以及擁有和瀏覽器同等的系統權限。
            • 例如,插件可使用C/C++編寫,通過插件可以獲取到操作系統任意資源;腳本也可以通過瀏覽器的漏洞來獲取系統權限,引發安全問題。

          多進程瀏覽器

          Chrome一問世便使用了多進程的架構,其頁面運行在了單獨的渲染進程中,插件運行在單獨的插件進行中,進程間使用IPC進行通信。

          瀏覽器的主要進程有哪些:

          • 瀏覽器進程。相當于瀏覽器的大腦,主要負責界面顯示、用戶交互、子進程管理,同時提供存儲等功能。
          • 渲染進程。核心任務是將 HTML、CSS 和 JavaScript 轉換為用戶可以與之交互的網頁,排版引擎 Blink 和 JavaScript 引擎 V8 都是運行在該進程中。

          默認情況下,Chrome 會為每個 Tab 標簽創建一個渲染進程。因為渲染進程所有的內容都是通過網絡獲取的,會存在一些惡意代碼利用瀏覽器漏洞對系統進行攻擊,所以運行在渲染進程里面的代碼是不被信任的。這也是為什么 Chrome 會讓渲染進程運行在安全沙箱里,就是為了保證系統的安全。

          • 網絡進程。主要負責頁面的網絡資源加載,之前是作為一個模塊運行在瀏覽器進程里面的,目前已獨立出來,成為一個單獨的進程。
          • 插件進程。主要是負責插件的運行,因插件易崩潰,所以需要通過插件進程來隔離,以保證插件進程崩潰不會對瀏覽器和頁面造成影響。
          • GPU 進程。當頁面使用了硬件加速時,會使用它來渲染頁面。

          其實,Chrome 剛開始發布的時候是沒有單獨 GPU 進程的,都是放到瀏覽器主進程中的。而 GPU 的使用初衷是為了實現 3D CSS 的效果,只是隨后網頁、Chrome 的 UI 界面都選擇采用 GPU 來繪制,這使得 GPU 成為瀏覽器普遍的需求。最后,Chrome 在其多進程架構上也引入了 GPU 進程。

          多進程瀏覽器是如何解決單進程瀏覽的問題的:

          • 【不穩定】正是由于進程之間相互隔離,當一個頁面或者插件崩潰時只會影響當前的進程,不會影響到瀏覽器和其他頁面。
          • 【不流暢】由于JS腳本運行在渲染進程中,即使JS阻塞了渲染進程,也只會影響當前頁面的渲染,而其他頁面的腳本則會運行在他們自己的渲染進程中,不受影響;此外,內存泄漏導致的不流暢問題也會隨著一個頁面的關閉導致一個進程的結束而解決。
          • 【不安全】多進程架構的安全沙箱,相當于是操作系統給進程上了一把鎖,沙箱中的程序可運行不可寫入、不可讀取敏感數據。

          多進程瀏覽器存在的問題:

          • 更高的資源占用。以Chrome瀏覽器為例,其將為每個頁面分配單獨的渲染進程,為每個插件分配單獨的插件進程,因此會消耗更多內存資源。
          • 更復雜的體系架構。瀏覽器各個模塊之間耦合度高、擴展性差目前的架構較難適應新需。

          2. 導航流程

          從用戶發出URL請求到頁面開始解析的過程,叫做導航,是網絡加載流程和渲染流程之間的橋梁。

          1. 首先,瀏覽器進程接收到用戶輸入的 URL 請求,瀏覽器進程便將該 URL 通過 IPC 轉發給網絡進程。
          2. 然后,在網絡進程中發起真正的 URL 請求。
          3. 接著網絡進程接收到了響應頭數據,便解析響應頭數據,并將數據轉發給瀏覽器進程。
          4. 瀏覽器進程接收到網絡進程的響應頭數據之后,發送“提交文檔 (CommitNavigation)”消息到渲染進程。
          5. 渲染進程接收到“提交文檔”的消息之后,便開始準備接收 HTML 數據,接收數據的方式是直接和網絡進程建立數據管道。
          6. 待網絡進程中文檔數據傳輸完成,渲染進程會向瀏覽器進程“確認提交”,這是告訴瀏覽器進程:“已經準備好接收和解析頁面數據了”。
          7. 瀏覽器進程接收到渲染進程“確認提交”的消息之后,導航流程就結束了。此時,渲染進程就會開始解析頁面和加載子資源了,瀏覽器進程將開始移除之前舊的文檔,然后更新瀏覽器進程中的頁面狀態。

          3. 渲染流程

          渲染流水線

          渲染流水線可分為如下幾個子階段:構建 DOM 樹、樣式計算、布局、分層、繪制、分塊、光柵化和合成。

          1. 構建DOM樹(DOM)
          • 瀏覽器無法直接理解和使用 HTML,所以要將其轉化為瀏覽器能夠理解的解構——經過 HTML 解析器解析,輸出樹狀結構的 DOM
          1. 樣式計算(Style)
          • 目的是計算DOM節點中的每個元素具體樣式,可分為三步
            • 渲染引擎把CSS文本轉為瀏覽器可理解的結構——styleSheets 樣式表
            • 標準化樣式表中的屬性值。這是由于渲染引擎無法理解CSS文本中的各種屬性值,這些值會被轉為標準化的計算值(例如{color: blue}{color: rgb(0, 0, 225)}{font-weight: bold}{font-weight: 700}
            • 計算出DOM樹中每個節點的具體樣式,計算過程遵守CSS的繼承和層疊規則,被保存在 ComputedStyle 結構內
          1. 布局階段(Layout)
          • 計算DOM樹中可見元素的幾何位置信息,包括創建布局樹布局計算兩個階段
            • 創建布局樹
              • 遍歷DOM樹中的所有需要渲染節點,并添加到布局樹中
              • 不可見的節點如 head 標簽下的全部內容,display: none的標簽等會被忽略

            • 布局計算
              • 計算DOM節點的位置坐標,布局運算的結果會被寫回布局樹中
          1. 分層(Layer)
          • 針對頁面中的復雜效果,例如復雜的3D變換、頁面滾動、z 軸排序等,渲染引擎將為特定節點生成專用的圖層,并生成一顆圖層樹(Layer Tree)
          • 擁有層疊上下文屬性的元素會被提升為單獨的一層;需要剪裁的地方也會被創建為單獨的圖層

          注意,并非布局樹的每個節點都包含一個圖層,一個節點可以直接或間接地屬于一個層,例如一個節點可以從屬于父節點的圖層

          1. 圖層繪制(Paint)
          • 渲染引擎會對圖層樹中每個圖層進行繪制,將一個圖層的繪制拆分成很多小的繪制指令,然后把這些指令按順序組成一個待繪制列表
          1. 柵格化(生成位圖)
          • 繪制列表準備好后,主線程將其提交給合成線程,實際的繪制操作由渲染引擎中的合成線程來完成
            • 合成線程會根據視口位置和大小,將圖層(layer)劃分為塊(圖塊 tile)
            • 合成線程會按照視口附近的圖塊來優先生成位圖,實際生成位圖的操作由柵格化(將圖塊轉換為位圖)來執行,圖塊是柵格化的最小單位
            • 渲染進程會維護一個柵格化的線程池,柵格化過程通常都會使用GPU來加速生成,使用GPU生成位圖的過程叫做快速柵格化,生成的位圖被保存在GPU內存中
          1. 合成與顯示
          • 所有圖塊都被柵格化后,合成線程將生成繪制圖塊命令 DrawQuad 提交給瀏覽器進程
          • 瀏覽器進程中 viz 組件接收 DrawQuad 命令,根據此命令,將其頁面內容繪制在內存中,最后再顯示到屏幕上

          流水線總結

          1. 渲染進程將 HTML 內容轉換為能夠讀懂的 DOM 樹結構。
          2. 渲染引擎將 CSS 樣式表轉化為瀏覽器可以理解的 styleSheets,計算出 DOM 節點的樣式。
          3. 創建布局樹,并計算元素的布局信息。
          4. 對布局樹進行分層,并生成分層樹
          5. 為每個圖層生成繪制列表,并將其提交到合成線程。
          6. 合成線程將圖層分成圖塊,并在光柵化線程池中將圖塊轉換成位圖。
          7. 合成線程發送繪制圖塊命令 DrawQuad 給瀏覽器進程。
          8. 瀏覽器進程根據 DrawQuad 消息生成頁面,并顯示到顯示器上。

          回流和重繪

          基于上述瀏覽器的渲染原理,我們可以理解回流和重繪是如何對瀏覽器性能造成影響的。由于瀏覽器渲染頁面默認使用流式布局模型,當某個DOM或CSS幾何屬性發生改變后,文檔流就會受到波動,就需要對DOM重新進行計算,重新布局頁面,引發回流。

          • 更新元素幾何屬性 —— 回流
            • 幾何屬性的修改會觸發瀏覽器重新布局(Layout & Layer),渲染樹需要重新生成,解析后來的一系列子階段
            • 因此回流需要更新完整的渲染流水線,開銷是最大的

          • 更新元素繪制屬性 —— 重繪
            • 繪制屬性的修改并沒有導致幾何位置的變化,所以不會導致布局階段的執行,會直接進入繪制階段,然后執行后來的子階段
            • 重繪操作相比回流省去了布局和分層階段,效率高于回流

          • GPU加速 —— 直接合成
            • 如果更改的屬性不需要布局和繪制,渲染引擎會跳過布局和繪制,直接進入非主線程——合成線程執行后續合成操作(比如利用 CSS3 的transformopacityfilter這些屬性就可以實現合成效果)
            • 例如,使用CSS transform實現動畫效果的渲染流水線如下:一是避開了重繪、回流,因此避開了布局和繪制階段;二是直接在非主線程執行合成動畫操作,未占用主線程資源。相比于重繪和回流,合成大大提升了繪制效率

          Reference

          [1] 瀏覽器工作原理與實踐:https://time.geekbang.org/column/intro/100033601

          [2] 瀏覽器進程架構的演化:https://zhuanlan.zhihu.com/p/96957235

          覽器渲染頁面有以下幾個步驟:

          1. 當瀏覽器的網絡進程接收到 HTML文檔后,會開啟一個渲染任務,并將其傳遞給渲染主線程的消息隊列。
          2. 在事件循環機制的作用下,渲染主線程會取出消息隊列中的渲染任務,開啟渲染流程。
          3. 整個的渲染流程分為多個階段,分別是:HTML 解析、樣式計算、布局、分層、繪制、分塊、光柵化、畫;
          4. 每個階段都有明確的輸入和輸出、上一個階段的輸出會成為下一個階段的輸入;這樣一來,整個渲染流程就形成了一套組織嚴密的生產流水線。

          載網絡,侵權必刪

          我們可能都知道瀏覽器含有一個渲染引擎,用來渲染窗口所展示的內容。默認情況下,渲染引擎可以顯示html、xml文檔及圖片,它也可以借助插件(一種瀏覽器擴展)顯示其他類型數據,例如使用PDF閱讀器插件,用于顯示PDF格式。但是其具體的渲染原理和流程估計也有很多人都不知道或者不清楚吧。這些天研究了一下瀏覽器的渲染原理,有了些心得,在這里跟大家分享一下,這里只討論渲染引擎最主要的用途——顯示應用了CSS之后的html及圖片。

          渲染引擎簡介

            本文所討論的瀏覽器——Firefox、Chrome和Safari是基于兩種渲染引擎構建的,Firefox使用Geoko——Mozilla自主研發的渲染引擎,Safari和Chrome都使用webkit。

          渲染主流程

            渲染引擎首先通過網絡獲得所請求文檔的內容,通常以8K分塊的方式完成。下面是渲染引擎在取得內容之后的基本流程:

            解析html以構建dom樹 -> 構建render樹 -> 布局render樹 -> 繪制render樹

           這里先解釋一下幾個概念,方便大家理解:

            DOM Tree:瀏覽器將HTML解析成樹形的數據結構。

            CSS Rule Tree:瀏覽器將CSS解析成樹形的數據結構。

            Render Tree: DOM和CSSOM合并后生成Render Tree。

            layout: 有了Render Tree,瀏覽器已經能知道網頁中有哪些節點、各個節點的CSS定義以及他們的從屬關系,從而去計算出每個節點在屏幕中的位置。

            painting: 按照算出來的規則,通過顯卡,把內容畫到屏幕上。

            reflow(回流):當瀏覽器發現某個部分發生了點變化影響了布局,需要倒回去重新渲染,內行稱這個回退的過程叫 reflow。reflow 會從 <html> 這個 root frame 開始遞歸往下,依次計算所有的結點幾何尺寸和位置。reflow 幾乎是無法避免的。現在界面上流行的一些效果,比如樹狀目錄的折疊、展開(實質上是元素的顯 示與隱藏)等,都將引起瀏覽器的 reflow。鼠標滑過、點擊……只要這些行為引起了頁面上某些元素的占位面積、定位方式、邊距等屬性的變化,都會引起它內部、周圍甚至整個頁面的重新渲 染。通常我們都無法預估瀏覽器到底會 reflow 哪一部分的代碼,它們都彼此相互影響著。

            repaint(重繪):改變某個元素的背景色、文字顏色、邊框顏色等等不影響它周圍或內部布局的屬性時,屏幕的一部分要重畫,但是元素的幾何尺寸沒有變。

          注意:(1)display:none 的節點不會被加入Render Tree,而visibility: hidden 則會,所以,如果某個節點最開始是不顯示的,設為display:none是更優的。

             (2)display:none 會觸發 reflow,而 visibility:hidden 只會觸發 repaint,因為沒有發現位置變化。

             (3)有些情況下,比如修改了元素的樣式,瀏覽器并不會立刻reflow 或 repaint 一次,而是會把這樣的操作積攢一批,然后做一次 reflow,這又叫異步 reflow 或增量異步 reflow。但是在有些情況下,比如resize 窗口,改變了頁面默認的字體等。對于這些操作,瀏覽器會馬上進行 reflow。

            來看看webkit的主要流程:

            再來看看Geoko的主要流程:


            Gecko 里把格式化好的可視元素稱做“幀樹”(Frame tree)。每個元素就是一個幀(frame)。 webkit 則使用”渲染樹”這個術語,渲染樹由”渲染對象”組成。webkit 里使用”layout”表示元素的布局,Gecko則稱為”reflow”。Webkit使用”Attachment”來連接DOM節點與可視化信息以構建渲染樹。一個非語義上的小差別是Gecko在HTML與DOM樹之間有一個附加的層 ,稱作”content sink”,是創建DOM對象的工廠。

            盡管Webkit與Gecko使用略微不同的術語,這個過程還是基本相同的,如下:

            1. 瀏覽器會將HTML解析成一個DOM樹,DOM 樹的構建過程是一個深度遍歷過程:當前節點的所有子節點都構建好后才會去構建當前節點的下一個兄弟節點。

            2. 將CSS解析成 CSS Rule Tree 。

            3. 根據DOM樹和CSSOM來構造 Rendering Tree。注意:Rendering Tree 渲染樹并不等同于 DOM 樹,因為一些像Header或display:none的東西就沒必要放在渲染樹中了。

            4. 有了Render Tree,瀏覽器已經能知道網頁中有哪些節點、各個節點的CSS定義以及他們的從屬關系。下一步操作稱之為layout,顧名思義就是計算出每個節點在屏幕中的位置。

            5. 再下一步就是繪制,即遍歷render樹,并使用UI后端層繪制每個節點。

            注意:上述這個過程是逐步完成的,為了更好的用戶體驗,渲染引擎將會盡可能早的將內容呈現到屏幕上,并不會等到所有的html都解析完成之后再去構建和布局render樹。它是解析完一部分內容就顯示一部分內容,同時,可能還在通過網絡下載其余內容。


          主站蜘蛛池模板: 久久精品一区二区| 久久福利一区二区| 波多野结衣精品一区二区三区| 波多野结衣中文字幕一区| 免费萌白酱国产一区二区三区| 欧洲精品一区二区三区| 国产在线观看一区二区三区精品| 国语对白一区二区三区| 人妻激情偷乱视频一区二区三区 | 极品人妻少妇一区二区三区 | 日本精品夜色视频一区二区| 中文字幕人妻无码一区二区三区| 无码视频一区二区三区| 亚洲人成人一区二区三区| 精品一区二区久久久久久久网站| 成人区人妻精品一区二区不卡视频| 精品欧洲av无码一区二区| 91久久精一区二区三区大全| 国产精品一区二区三区久久| 国产成人精品无码一区二区三区 | 免费一本色道久久一区| 一区二区传媒有限公司| 奇米精品视频一区二区三区| 中文字幕一区二区三区永久| 视频在线观看一区二区| 精品国产一区二区三区不卡| 一区二区三区日韩精品| 亚洲AV无码一区二区三区DV| 亚洲精品色播一区二区| 午夜肉伦伦影院久久精品免费看国产一区二区三区 | 大香伊人久久精品一区二区| 日韩电影一区二区三区| 免费av一区二区三区| 久久伊人精品一区二区三区| 中文字幕永久一区二区三区在线观看 | 国产精品538一区二区在线| 国产午夜精品一区二区三区极品 | 国产精品一区二区久久精品无码| 无码少妇一区二区| 国产精品无码亚洲一区二区三区| 狠狠色成人一区二区三区|