瀏覽器進程?執行緒?傻傻分不清楚!

  • 2019 年 12 月 4 日
  • 筆記

在生活中,瀏覽器和我們的工作和生活息息相關。做為前端開發,我們程式碼的應用場景往往是在瀏覽器上。瀏覽器對前端的重要性不可一日而語。那麼我們對瀏覽器是否有比較清晰的了解呢?什麼是多進程架構瀏覽器?為什麼瀏覽器內核是多執行緒?Javascript是單執行緒又是什麼鬼?進程和執行緒是否分得清楚呢?

進程(process)和執行緒(thread)

進程和執行緒是作業系統的基本概念,許多人會有所了解,但不能較為清晰的分辨。 這裡我們需要了解下面幾個點。

CPU

CPU是電腦的核心,其負責承擔電腦的計算任務。這裡我們比喻為一個工廠

進程

學術上說,進程是一個具有一定獨立功能的程式在一個數據集上的一次動態執行的過程,是作業系統進行資源分配和調度的一個獨立單位,是應用程式運行的載體。我們這裡將進程比喻為工廠的車間,它代表CPU所能處理的單個任務。任一時刻,CPU總是運行一個進程,其他進程處於非運行狀態。

執行緒

在早期的作業系統中並沒有執行緒的概念,進程是能擁有資源和獨立運行的最小單位,也是程式執行的最小單位。任務調度採用的是時間片輪轉的搶佔式調度方式,而進程是任務調度的最小單位,每個進程有各自獨立的一塊記憶體,使得各個進程之間記憶體地址相互隔離。後來,隨著電腦的發展,對CPU的要求越來越高,進程之間的切換開銷較大,已經無法滿足越來越複雜的程式的要求了。於是就發明了執行緒,執行緒是程式執行中一個單一的順序控制流程,是程式執行流的最小單元。這裡把執行緒比喻一個車間的工人,即一個車間可以允許由多個工人協同完成一個任務。

進程和執行緒的區別和關係

  • 進程是作業系統分配資源的最小單位,執行緒是程式執行的最小單位。
  • 一個進程由一個或多個執行緒組成,執行緒是一個進程中程式碼的不同執行路線;
  • 進程之間相互獨立,但同一進程下的各個執行緒之間共享程式的記憶體空間(包括程式碼段、數據集、堆等)及一些進程級的資源(如打開文件和訊號)。
  • 調度和切換:執行緒上下文切換比進程上下文切換要快得多

多進程和多執行緒

  • 多進程:多進程指的是在同一個時間裡,同一個電腦系統中如果允許兩個或兩個以上的進程處於運行狀態。多進程帶來的好處是明顯的,比如你可以聽歌的同時,打開編輯器敲程式碼,編輯器和聽歌軟體的進程之間絲毫不會相互干擾。
  • 多執行緒是指程式中包含多個執行流,即在一個程式中可以同時運行多個不同的執行緒來執行不同的任務,也就是說允許單個程式創建多個並行執行的執行緒來完成各自的任務。

瀏覽器多進程架構

跟現在的很多多執行緒瀏覽器不一樣,Chrome瀏覽器使用多個進程來隔離不同的網頁。因此在Chrome中打開一個網頁相當於起了一個進程

那麼Chrome為什麼要使用多進程架構?

在瀏覽器剛被設計出來的時候,那時的網頁非常的簡單,每個網頁的資源佔有率是非常低的,因此一個進程處理多個網頁時可行的。然後在今天,大量網頁變得日益複雜。把所有網頁都放進一個進程的瀏覽器面臨在健壯性,響應速度,安全性方面的挑戰。因為如果瀏覽器中的一個tab網頁崩潰的話,將會導致其他被打開的網頁應用。另外相對於執行緒,進程之間是不共享資源和地址空間的,所以不會存在太多的安全問題,而由於多個執行緒共享著相同的地址空間和資源,所以會存在執行緒之間有可能會惡意修改或者獲取非授權數據等複雜的安全問題。

在了解這個知識點線,我們需要先說明下什麼是瀏覽器內核

瀏覽器內核

簡單來說瀏覽器內核是通過取得頁面內容、整理資訊(應用CSS)、計算和組合最終輸出可視化的影像結果,通常也被稱為渲染引擎。從上面我們可以知道,Chrome瀏覽器為每個tab頁面單獨啟用進程,因此每個tab網頁都有由其獨立的渲染引擎實例。

瀏覽器內核是多執行緒

瀏覽器內核是多執行緒,在內核控制下各執行緒相互配合以保持同步,一個瀏覽器通常由以下常駐執行緒組成:

  • GUI 渲染執行緒
  • JavaScript引擎執行緒
  • 定時觸發器執行緒
  • 事件觸發執行緒
  • 非同步http請求執行緒

GUI渲染執行緒

GUI渲染執行緒負責渲染瀏覽器介面HTML元素,當介面需要重繪(Repaint)或由於某種操作引發迴流(reflow)時,該執行緒就會執行。在Javascript引擎運行腳本期間,GUI渲染執行緒都是處於掛起狀態的,也就是說被」凍結」了.

Javascript引擎執行緒

Javascript引擎,也可以稱為JS內核,主要負責處理Javascript腳本程式,例如V8引擎。Javascript引擎執行緒理所當然是負責解析Javascript腳本,運行程式碼。

Javascript是單執行緒的

Javascript是單執行緒的, 那麼為什麼Javascript要是單執行緒的?

這是因為Javascript這門腳本語言誕生的使命所致:JavaScript為處理頁面中用戶的交互,以及操作DOM樹、CSS樣式樹來給用戶呈現一份動態而豐富的交互體驗和伺服器邏輯的交互處理。如果JavaScript是多執行緒的方式來操作這些UI DOM,則可能出現UI操作的衝突; 如果Javascript是多執行緒的話,在多執行緒的交互下,處於UI中的DOM節點就可能成為一個臨界資源,假設存在兩個執行緒同時操作一個DOM,一個負責修改一個負責刪除,那麼這個時候就需要瀏覽器來裁決如何生效哪個執行緒的執行結果。當然我們可以通過鎖來解決上面的問題。但為了避免因為引入了鎖而帶來更大的複雜性,Javascript在最初就選擇了單執行緒執行。

GUI 渲染執行緒 與 JavaScript引擎執行緒互斥!

由於JavaScript是可操縱DOM的,如果在修改這些元素屬性同時渲染介面(即JavaScript執行緒和UI執行緒同時運行),那麼渲染執行緒前後獲得的元素數據就可能不一致了。因此為了防止渲染出現不可預期的結果,瀏覽器設置GUI渲染執行緒與JavaScript引擎為互斥的關係,當JavaScript引擎執行時GUI執行緒會被掛起,GUI更新會被保存在一個隊列中等到引擎執行緒空閑時立即被執行。

JS阻塞頁面載入

從上面我們可以推理出,由於GUI渲染執行緒與JavaScript執行執行緒是互斥的關係,當瀏覽器在執行JavaScript程式的時候,GUI渲染執行緒會被保存在一個隊列中,直到JS程式執行完成,才會接著執行。因此如果JS執行的時間過長,這樣就會造成頁面的渲染不連貫,導致頁面渲染載入阻塞的感覺。

定時觸發器執行緒

瀏覽器定時計數器並不是由JavaScript引擎計數的, 因為JavaScript引擎是單執行緒的, 如果處於阻塞執行緒狀態就會影響記計時的準確, 因此通過單獨執行緒來計時並觸發定時是更為合理的方案。

事件觸發執行緒

當一個事件被觸發時該執行緒會把事件添加到待處理隊列的隊尾,等待JS引擎的處理。這些事件可以是當前執行的程式碼塊如定時任務、也可來自瀏覽器內核的其他執行緒如滑鼠點擊、AJAX非同步請求等,但由於JS的單執行緒關係所有這些事件都得排隊等待JS引擎處理。

非同步http請求執行緒

在XMLHttpRequest在連接後是通過瀏覽器新開一個執行緒請求, 將檢測到狀態變更時,如果設置有回調函數,非同步執行緒就產生狀態變更事件放到 JavaScript引擎的處理隊列中等待處理。

相關閱讀

進程與執行緒的一個簡單解釋