必知必會的大廠面試計算機基礎和概念

最近在翻閱文章時,看到朋友推薦的《程序員的自我修養》,這是一本講連接、裝載與庫的計算機圖書,看了下目錄後以爲挺有意思。程序員

所以決定每讀一章就將其讀書筆記整理記錄下來,分享給你們。網絡

目錄:多線程

image

不要讓 CPU 打盹

在計算機發展早期,CPU 資源十分昂貴。若是一個 CPU 只能運行一個程序,那麼當程序在讀寫磁盤(進行 I/O 操做)時,CPU 就空閒下來了。這在當時簡直就是巨大的浪費。併發

image

CPU 只能和一個程序A 「聊天「,其餘來再多的程序BCD,都沒有任何操做的空間。就像早年的手機,打電話和上網(語音/數據)只能二選一,做爲 CPU 的你,並不能多線程操做。性能

所以機智的人們很快就編寫了一些監控程序,但願來解決這個問題。spa

多道程序(Multiprogramming)

多道程序起,操做系統正式具備同時運行多個程序的能力。操作系統

其是讓 CPU 一次讀取多個程序放入內存中。當某個程序暫時無須使用 CPU 時,監控程序就把另外的正在等待 CPU 資源的程序啓動,以此使得 CPU 可以充分地利用起來。這種策略的確大大的提升了 CPU 資源的利用率。線程

真實場景

你在 Windows 上點擊鼠標 10 分鐘之後系統纔有反應,那是多麼無奈的事情。由於沒有優先級區分,天然一路排下來也就不知道要等到何時了,至關於半餓死。設計

存在的問題

核心問題在於程序之間的調度策略太粗糙。對於多道程序來刪,程序之間部分輕重緩急,也就是說不存在優先級的區分。所以若是有些程序急需使用 CPU 來完成一些任務,那麼頗有可能會很長時間後纔有機會被分配到 CPU,才得以繼續往下運行。3d

分時系統(Time-Sharing System)

程序運行模式改成協做的模式,在原有的多道程序繼續升級改造,即每一個程序運行一段時間之後都主動讓出 CPU 給其餘程序,使得一段時間內每一個程序都有機會運行一小段。

真實場景

好比你點擊一下鼠標或按下一個鍵盤按鍵後,他會相較前者可以更快的獲得響應,由於他好歹是存在切換的可能性。

存在問題

這時候的監控程序已經比原有多道程序的模式已經複雜了很多,完整的操做系統雛形已經基本造成,很早期的 Windows(Windows 95 和 Windows NT 以前),MacOS X 以前的 MacOS 版本都是採用這種分時系統的方式來進行程序調度。

其仍然存在問題,核心在於若一個程序一直在進行一個耗時計算,便會一直霸佔着 CPU 不放,那麼操做系統也沒有不放,就會致使其餘程序都只能無限等待,至關於就是系統假死了。

多任務系統(Multi-tasking)

背景

在分時系統中,一個程序死循環就會致使系統假死,而且其運行效率並不高,只能解決當時的交互式環境。

放在如今來說,已經徹底無法很好的運行。所以當時業界也在研究更爲先進的操做系統模式,也就是如今最爲流行也是最熟悉的多任務系統。

解決方案

在多任務系統中,全部的應用程序都以進行(Process)的方式運行,其有如下特色:

  • 每一個進程都有本身獨立的地址空間,所以各進程之間相互隔離。
  • 每一個 CPU 都由操做系通通一進行分配。
  • 每一個進程根據其優先級的高低都有機會獲得 CPU。

但須要注意的是,如果進程運行超出了指定的時間,操做系統就會暫停該進程,將 CPU 資源分配給其餘等待運行的進程。這種 CPU 的分配方式通常稱做搶佔式(Preemptive)。

經過這種方式,操做系統就能夠強制剝奪 CPU 資源而且分配給它認爲目前最須要資源的進程,若是分配給每一個進程的時間都很短,即 CPU 在多個進程間快速切換,就能夠形成多個進程同時在運行的假象。

內存不夠用怎麼辦

在早期的計算機中,程序是直接運行在物理內存上的,訪問的內存地址都是物理地址。假設只是一個進程在跑,可能內存資源還夠用,但實際上爲了更有效地利用硬件資源,咱們必須運行多個程序,CPU 的利用率纔會比較高。這時候就會遇到一個嚴重的問題,那就是如何將計算機上有限的物理內存分配給多個程序使用?

image

就像上圖,每一個程序他都想申請 1GB 的內容,而計算機自己只有 1GB 的物理內存,根本沒有辦法真正的執行。

真實場景和問題

可能會有小夥伴想,煎魚你舉的例子太極端了,咱們舉個 」正常「 點的例子。假設計算機有 128MB 內存,程序 A 運行須要 10MB,程序 B 須要 100MB,程序 C 須要 20 MB。假設該幾個程序運行時,咱們按照其想要的一分配,不就行了嗎?

但現實並非這樣,這種簡單的內存分配策略存在許多的問題:

  • 地址空間不隔離:全部程序都直接訪問物理地址,各程序所使用的內存空間並非相互獨立,很容易改寫到其餘程序的內存地址。
  • 內存使用效率低:沒有有效的內存管理機制,一會運行程序 A,一會運行程序 B,就須要常常要將大量數據換出換入,效率十分低下。
  • 程序運行的地址不肯定:每次程序運行時,都須要從內存中給其分配一塊足夠大的空閒區域,但這些內存區域位置是不肯定的,給程序編寫形成了必定的麻煩。

解決方法

解決上述問題的解決思路,就是萬能的法寶:增長中間層,即便用一種間接的地址訪問方法。把程序給出的地址看做是一種虛擬地址(Virtual Address),而後經過某些映射的方法,將這個虛擬地址最終轉換成實際的物理地址。

上述提到了兩個很是重要的內存概念:

  • 物理地址空間:是實實在在存在的,存在於計算機中,且對每一臺計算機來講只有惟一的一個,你能夠將其想象爲物理內存。
  • 虛擬地址空間:是指虛擬的、人們想象出來的地址空間。其實它並不存在,每一個進程都有本身的獨立虛擬空間,且每一個進程只能訪問本身的地址空間。

如此一來,操做系統只須要控制虛擬地址到物理地址的映射過程,就能夠保證任意一個程序鎖你訪問的物理內存區域和另一個程序不重疊,以達到地址空間隔離的效果。

進程虛擬空間、物理空間和磁盤之間的頁映射關係

另外須要清楚虛擬存儲的實現須要依靠硬件的支持,對於不一樣的 CPU 來講不一樣。但大多采用 MMU(Memory Management Unit)的部件來進行頁映射:

虛擬地址到物理地址的轉變

CPU 發出的是虛擬地址(Virtual Address),也就是平常程序中所看到的是虛擬地址。通過 MMU 轉換後就會變成物理地址(Physical Address)。

目前常見的 MMU 均已集成在 CPU 內部了,不會再以獨立部件存在。

線程的那些事

線程(Thread),有時候被稱爲輕量級進程,是程序執行流程的最小單元。一個標準的線程由線程 ID、當前指令指針(PC)、寄存器集合和堆棧組成。

一般一個進程由一個到多個線程組成,各個線程之間共享程序的內存空間(包括代碼段、數據段、堆等)及一些進程級的資源(如打開文件和信號)。

image

爲何須要多線程

  • 某個操做可能會陷入長時間等待,等待的線程會進入睡眠狀態,沒法繼續執行。多線程執行能夠有效利用等待的時間。典型的例子是等待網絡響應,這時候就能夠切換了。
  • 某個操做會消耗大量的時間,若是隻有一個線程,程序和用戶之間的交互會中斷。多線程的狀況下,可讓一個線程負責交互,另一個線程負責計算。
  • 程序邏輯自己就要求併發操做,例如一個多端下載軟件。
  • 多 CPU 或多核計算機,其自己就具有同時執行多個線程的能力。
  • 相對於多進程應用,多線程在數據共享方面效率會高不少。

線程的訪問權限

線程能夠訪問進程內存裏的全部數據,甚至在知道堆棧地址的狀況下,能夠訪問其餘線程裏的堆棧信息。其私有存儲空間主要分爲:棧、線程局部存儲(Thread Local Storage,TLS)、寄存器(包括 PC 寄存器)。

線程調度和時間片

在單處理器對應多線程的狀況下,併發是一種模擬處理的狀態。操做系統會讓這些多線程程序輪流執行,每次僅執行一小段時間(一般是幾十到幾百毫秒),這樣子線程就 「看起來」 在同時執行。

不斷在處理器上切換不一樣的線程行爲稱之爲線程調度(Thread Schedule),一般擁有至少三種狀態,分別是:

  • 運行(Running):此時線程正在執行。
  • 就緒(Ready):此時線程能夠馬上運行,但 CPU 已經被佔用,暫時沒法分配。
  • 等待(Waiting):此時線程正在等待某一事件(例如:I/O 事件)發生,沒法執行。

處於運行狀態中的線程都會擁有一段能夠執行的時間,這段時間段稱爲時間片(Time Slice)。其基本流轉:

  • 當時間片用盡的時候,進程進入就緒狀態。
  • 當時間片用盡以前,進程若開始等待某個事件,那麼它將進入等待狀態。

每當一個線程離開運行狀態時,調度系統就會選擇一個當前是就緒狀態的線程繼續執行。而一個處於等待狀態的線程在完成所等待的事件後,就會進入就緒狀態。

image

線程優先級

在 Windows 和 Linux 中,線程的優先級能夠經過用戶手動設置,系統也會根據線程的表現自動調整優先級,以使得調度更有效率。常見的通常有兩類線程:

  • IO 密集型線程(IO Bound Thread):頻繁等待,像是網絡調用。
  • CPU 密集型線程(CPU Bound Thread):不多等待,主要是計算爲主。

常見的線程調度方式以下:

  • 輪轉法:讓各個線程輪流執行一小段時間,這也決定了線程之間存在交錯執行的特色。
  • 優先級調度:在具備優先級調度的系統中,線程都擁有各自的線程優先級,具備高優先級的線程會更早的執行,低優先級的線程經常要等待到系統中已經沒有高優先級的可執行線程時才能夠執行。

IO 密集型線程老是會比 CPU 密集型線程容易獲得優先級的提高。但在優先級調度下,存在一種線程餓死的現象。一個線程被餓死,是說它的優先級比較低,在它執行以前,老是有較高優先級的線程要執行。所以低優先級線程始終沒法執行。

爲了不餓死現象,調度系統會逐步提高那些等待了過長時間的得不到執行的線程優先級。這樣的方式,一個線程只要等待足夠長的時間,其優先級最終必定會提升到足夠讓他執行的程度。線程優先級改變通常有三種方式:

  • 用戶指定優先級。
  • 根據進入等待狀態的頻繁程度提高或下降優先級。
  • 長時間得不到執行而被提高優先級。

可搶佔線程和不可搶佔線程

線程在用盡時間片以後會被強制剝奪繼續執行的權利,而進入就緒狀態,這個過程叫作搶佔(Preemption),即以後執行的別的線程搶佔了當前線程。

目前以可搶佔式線程居多,非搶佔式線程在今日已經十分少見。

三種線程模型

平常在程序中使用的線程其實並非內核線程,而是存在於用戶態的用戶線程。用戶態並不必定在操做系統內核中對應同等數量的內核線程。接下來將介紹三種常見的用戶態多線程庫的實現方式。

一對一模型

一對一模型指的是一個用戶使用的線程就惟一對應一個內核使用的線程。

一對一線程模型

優勢:

  • 線程之間的併發是真正的併發。
  • 線程阻塞時,其餘線程執行不會受到影響。

缺點:

  • 操做系統限制了內核線程的數量,所以一對一線程會讓用戶的線程數受限。
  • 內核線程調度時,上下文切換的開銷較大,致使用戶線程的執行效率降低。

多對一模型

多對一模型指的是多個用戶線程映射到一個內核線程上,線程之間的切換由用戶態的代碼來進行。

多對一模型

優勢:

  • 線程切換相對於一對一模型來講高效許多。

缺點:

  • 若是一個用戶線程阻塞,那麼全部的線程豆漿沒法執行,由於內核線程也被阻塞住了。

多對多模型

多對多模型指的是將多個用戶線程映射到少數但不止一個內核線程上。

多對多模型

優勢:

  • 一個用戶線程阻塞,並不會致使其它用戶線程也阻塞。

缺點:

  • 性能提高不如一對一模型高。

總結

本文主要涉及到 CPU、內存、線程。咱們可以從其的一些關注點知道爲何 CPU 調度會這樣子發展,又經歷了什麼東西。內存爲何會出現虛擬內存,物理內存,其之間又是如何相互轉換的。

另外還了解到線程的基本分類和常見調度方式等,這些都是計算機基本的軟硬件知識,很是值得你們仔細思考。

相關文章
相關標籤/搜索