1. 程式人生 > 實用技巧 >ICS第一章:計算機系統漫遊

ICS第一章:計算機系統漫遊

ICS第一章:計算機系統漫遊

來自於機械工業出版社《深入理解計算機系統(第三版)》第一章~

計算機系統由硬體系統軟體組成,它們共同工作來執行程式。

資訊就是位+上下文

下面為一個簡單的\(hello\)程式:

#include <stdio.h>

int main()
{
    printf("hello,world\n");
    return 0;
}

對於上述的\(hello\)程式,其生命週期是由一個源程式開始的,即\(hello.c\),這個源程式即為程式設計師通過編輯器建立的文字檔案,實際上是一個由值0和1組成的位序列。

8個位被組織成一組,稱為位元組

大部分的現代計算機以\(ASCII\)

標準來表示文字字元,即用一個唯一的單位元組大小的整數值來表示每個字元。例如對於上述\(hello\)程式,其\(ASCII\)碼錶示如下:

注意:每個文字行都是以一個看不見的換行符'\n'來結束的,對應的整數值為10。向\(hello.c\)這樣只由\(ASCII\)字元構成的檔案稱為文字檔案,所有其它檔案都稱為二進位制檔案

\(hello.c\)的表示方法說明了一個基本思想:系統中的所有的資訊——包括磁碟檔案、記憶體中的程式、記憶體中存放的使用者資料以及網路上傳送的資料,都是由一串位元來表示的。

C語言成功的原因:

  • C語言與Unix作業系統關係密切;
  • C語言小而簡單;
  • C語言是為實踐目的設計的。

程式被其它程式翻譯成不同格式

要想在系統上執行\(hello.c\)程式,需要將其中每條\(C\)語句轉化為一系列的低階機器語言指令,然後將這些指令按照可執行目標程式的格式進行打包,以二進位制磁碟檔案的形式存放。

編譯系統\(Compilation\ system\):前處理器、編譯器、彙編器、連結器。

GCC編譯器驅動程式讀取源程式檔案\(hello.c\),將其翻譯為一個可執行目標檔案\(hello\)。這個翻譯過程的四個階段如下:

  • 預處理階段。前處理器(cpp)根據以字元#開頭的命令來對原始的\(C\)程式進行修改。通常得到的檔案以\(.i\)作為檔案拓展名。
  • 編譯階段。編譯器(ccl)通過組合語言程式
    將文字檔案\(hello.i\)翻譯為文字檔案\(hello.s\)
  • 彙編階段。彙編器(as)將\(hello.s\)發明以為機器語言指令,打包為一種叫做可重定位目標程式的格式,將結果儲存在目標檔案\(hello.o\)中,\(hello.o\)為一個二進位制檔案。
  • 連結階段。由於\(hello\)程式中帶哦用了標準\(C\)庫的一個函式\(printf\)函式,而\(printf\)函式存在於一個名為\(printf.o\)的單獨的預編譯好的目標檔案中。連結器(ld)負責將\(prinf.o\)檔案與\(hello.o\)檔案進行合併,從而得到可執行檔案\(hello\),它可以被載入到記憶體中由系統執行。

瞭解編譯系統如何工作是大有益處的

對於簡單的程式,可以依靠編譯系統來生成有效的機器程式碼。但程式設計師需要指導編譯系統的工作原理的原理主要有:

  • 優化程式效能。瞭解一些機器程式碼以及編譯器將不同的\(C\)語言轉化為機器程式碼的方式。
  • 理解連結時出現的錯誤。有關連結器的操作很容易引起錯誤,通過了解編譯系統的工作原理,可以是我們更好的理解。
  • 避免安全漏洞。緩衝區溢位錯誤是造成大多數網路和\(Internet\)伺服器上安全漏洞的主要原因,瞭解資料與控制資訊儲存的原理可以幫助我們避免安全漏洞。

處理器讀並解釋儲存在記憶體上的指令

系統的硬體組成

  1. 匯流排。匯流排指貫穿整個系統的一組電子管道,其攜帶資訊位元組並負責在各部件之間傳遞。通常匯流排被設計成傳送定長的位元組塊,也就是字(word)。

  2. I/O裝置。I/O即指輸入/輸出,I/O裝置是系統與外部世界的聯絡通道。在示例系統中,包括四個I/O裝置(在圖中下方黑色箭頭標明):

    • 作為使用者輸入的鍵盤;
    • 作為使用者輸入的滑鼠;
    • 作為使用者輸出的顯示器;
    • 用於長期儲存資料和程式的磁碟驅動器(即磁碟)。

    對於每一個I/O裝置,通過一個控制器或介面卡與I/O匯流排相連。(如圖中的USB控制器、圖形介面卡、磁碟控制器)

    控制器與介面卡的主要區別:封裝方式

    • 控制器是I/O裝置本身或系統的主機板(主印刷電路板)上的晶片組;
    • 介面卡是一塊插在主機板插槽上的卡。

    控制器與介面卡的主要功能:在I/O匯流排和I/O裝置之間傳遞資訊

  3. 主存。主存是一個臨時儲存裝置,在處理器執行程式時,用於存放程式和程式處理的資料。

    • 從邏輯上來說。儲存器是一個線性的位元組陣列,每個位元組都有其唯一的由零開始的地址(陣列索引)。
    • 從物理上來說。主存是由一組動態隨機存取儲存器(DRAM)晶片組成的。
  4. 處理器。中央處理單元(CPU),簡稱處理器,是解釋或執行儲存在主存中指令的引擎。處理器的核心是一個大小為一個字的儲存裝置(暫存器),稱為程式計數器(PC),它指向主存中的某條機器語言指令。CPU在指令的要求下可能會執行下面的動作:

    • 載入。從主存複製一個位元組或者一個字到暫存器,覆蓋暫存器原有內容。
    • 儲存。從暫存器複製一個位元組或一個字到主存,覆蓋主存原有內容。
    • 操作。把兩個暫存器內容複製到ALU,對其進行算術運算,將結果替換其中一個暫存器的原有內容。
    • 跳轉。從指令本身抽取一個字,將其複製到PC中,以覆蓋PC中原有值。

執行\(hello\)程式

  • 從鍵盤上讀取\(hello\)命令。初始時,\(shell\)程式執行指令,等待使用者輸入命令,當通過鍵盤輸入字串"./hello"後,\(shell\)程式將字元讀入暫存器,再存放在記憶體中。
  • 從磁碟載入可執行檔案到主存。在鍵盤上敲下回車後,命令輸入完成,利用直接儲存器存取(DMA)技術,資料不通過處理器而直接由磁碟到達主存。
  • 將輸出字串從儲存器寫到顯示器。目標檔案\(hello\)的程式碼和資料被載入到主存,處理器開始執行機器語言指令,將需輸出的字串中的位元組從主存複製到暫存器檔案,再從暫存器檔案中複製到顯示裝置,最終顯示在螢幕上。

快取記憶體至關重要

系統花費了很多時間用於將資訊在各個地方之間進行復制,這些開銷減緩了工作的效率,因此需要考慮如何使這些賦值操作儘可能快地完成。

一個典型的暫存器檔案只儲存擊敗位元組地資訊,相比主存裡可存放幾億位元組地資訊顯得非常少,但處理器從暫存器中讀取資料的速度比從主存中讀取速度塊近百倍。由於處理器與主存之間的差異,催生了快取記憶體儲存器(cache memory,簡稱為cache)的誕生。

cache作為暫時的集結區域,存放處理器近期可能會需要的資訊。通過一種名為靜態隨機訪問儲存器(SRAM)的硬體技術,可以實現L1快取記憶體L2快取記憶體。其中程序訪問前者的速度幾乎與訪問暫存器檔案一樣快,訪問後者的速度約為前者的\(\frac{1}{5}\),但仍比訪問主存快5~10倍。位於處理器晶片的L1快取記憶體的容量可達數萬位元組,而L2快取記憶體通過一條特殊的匯流排連結至處理器,容量可達數十萬到數百萬。

儲存裝置形成層次結構

計算機系統的儲存裝置被組織為一個儲存器層次結構。由L0~L6,裝置的訪問速度依次變慢,容量依次變大。

儲存器層次結構的主要思想:上一層的儲存器作為低一層的儲存器的快取記憶體。如:暫存器檔案就是L1的快取記憶體,L1是L2的快取記憶體,L2是L3的快取記憶體,L3是主存的快取記憶體,而主存又是磁碟的快取記憶體。在某些具有分散式檔案系統的網路系統中,本地磁碟就是儲存在其他系統中磁碟上的資料的快取記憶體。

作業系統管理硬體

可以把作業系統視為應用程式和硬體之間插入的一層軟體,所有應用程式對硬體的操作嘗試都必須通過作業系統。

作業系統通過程序、虛擬記憶體、檔案等基本的抽象概念,可以實現兩個基本功能:

  1. 防止硬體被失控的應用程式濫用;
  2. 嚮應用程式提供簡單一致的機制來控制複雜而又通常大不相同的低階硬體裝置。

程序

程序是電腦科學中最重要和最成功的概念之一,程序是作業系統對一個正在執行的程式的一種抽象。

一個CPU看上去都像是在併發地執行多個程序,這是通過處理器在程序間切換來實現的,作業系統實現這種交錯執行的機制稱為上下文切換,即儲存當前程序的上下文、恢復新程序的上下文,然後將控制權傳遞到新程序,新程序就會從之前停止的地方開始。作業系統保持跟蹤程序執行所需的所有狀態資訊,這種狀態,也就是上下文,比如PC和暫存器檔案的當前值、主存的內容等。

對於示例場景而言,\(shell\)程序和\(hello\)程序是併發的,有一個程序到另一個程序的轉換是由作業系統核心(kernel)管理的。核心是作業系統程式碼常駐主存的部分。當應用程式需要作業系統的某些操作時(比如讀寫檔案),它就執行一條特殊的系統呼叫(system call)指令,將控制權傳遞給核心。然後核心執行被請求的操作並返回應用程式。

實現程序這個抽象概念需要低階硬體和作業系統軟體之間的緊密合作。'

執行緒

每個程序由多個執行單元構成,每個執行單元稱為執行緒

  • 每個執行緒都執行在程序的上下文中,並共享同樣的程式碼和全域性資料;
  • 多執行緒之間比多程序之間更容易共享資料;
  • 執行緒一般來說比程序更高效;
  • 在有多處理器可用時,多執行緒可使得程式執行更快。

虛擬記憶體

每個程序看到的記憶體是一致的,稱為虛擬地址空間。Linux程序的虛擬地址空間如下圖所示:

其中每個區都有專門的功能:

  • 程式程式碼和資料。程式碼是從同一固定地址開始,緊接著的是和全域性變數相對應的資料位置。
  • 堆。當呼叫像ma1loc和free這樣的C標準庫函式時,堆可以在執行時動態地擴充套件和收縮。
  • 共享庫。大約在地址空間的中間部分是一塊用來存放像C標準庫和數學庫這樣的共享庫的程式碼和資料的區域。
  • 棧。位於使用者虛擬地址空間頂部的是使用者棧,編譯器用它來實現函式呼叫。和堆一樣,使用者棧在程式執行期間可以動態地擴充套件和收縮。
  • 核心虛擬記憶體。是為核心保留的,不允許應用程式讀寫這個區域的內容或直接呼叫核心程式碼定義的函式。

檔案

檔案就是位元組序列。每個I/O裝置,包括磁碟、鍵盤、顯示器,甚至網路,都可以看成是檔案。系統中的所有輸入輸出都是通過使用一小組稱為Unix I/O的系統函式呼叫讀寫檔案來實現的。

系統之間利用網路通訊

之間的討論把系統視為一個孤立的硬體和軟體的集合體,實際上,現代系統經常通過網路與其它系統相連線。

對於單一的系統而言,可將網路視為一個I/O裝置:

下面是一種十分典型的客戶端與伺服器之間的互動形式,使用熟悉的telnet應用在一個遠端主機上執行hello程式:

重要主題

系統不僅只是硬體,二十硬體和系統軟體相互交織的集合體,它們共同協作以達到執行應用程式的最終目的。

\(Amdahl\)定律

計算領域的早期先鋒之一\(Gene\ Amdahl\)對提升系統一部分效能所帶來的效果做出了觀察,當我們對系統的某個部分加速時,其對系統整體效能的影響取決於該部分的重要性和加速程度,這就是\(Amdahl\)定律。

若系統執行某應用程式籌要時間為\(T_{old}\)。假設系統某部分所需執行時間與該時間的比例為 \(\alpha\),而該部分效能提升比例為\(k\)。即該部分初始所需時間為 \(\alpha T_{\text {old}}\),現在所 需時間為( \(\left.\alpha T_{\text {old }}\right) / k\) 。因此,總的執行時間應為

\[T_{\text {new }}=(1-\alpha) T_{\text {old }}+\left(\alpha T_{\text {old }}\right) / k=T_{\text {old }}[(1-\alpha)+\alpha / k] \]

由此可計算加速比\(S=Y_{old}/T_{new}\)

\[S=\frac{1}{(1-\alpha)+\alpha / k} \]

可以發現,雖然對系統的一個主要部分做出了重大改進,但是獲得的系統加速比卻明顯小於這部分的加速比,故想要顯著加速整個系統,必須提升全系統中相當大部分的速度

表示效能最好的方法就是採用相對效能,通過比例\(T_{old}/T_{new}\)的形式表示。

併發和並行

  • 併發(concurrency):指一個同時具有多個活動的系統;
  • 並行(parallelism):指用併發來使一個系統執行得更快。

按照系統層次結構由高到低的順序有三個層次:

  1. 執行緒級併發超執行緒有時候也叫做同時多執行緒,是一項允許一個CPU執行多個控制流的技術。超執行緒技術把多執行緒處理器內部的兩個邏輯核心模擬成兩個物理晶片,可以讓單個處理器使用執行緒級的平行計算,利用空閒CPU資源,在相同時間內完成更多工作。
  2. 指令級並行。現代處理器可以同時執行多條指令的屬性叫做指令級並行。如果處理器可以達到比一個週期一條指令更快的執行速率,就稱之為超標量處理器,大多數現代處理器都支援超標量操作。
  3. 單指令、多資料並行。有的處理器的硬體執行一條指令產生多個可以並行執行的操作,這種方式叫做單指令、多資料。

計算機系統中抽象的重要性

在學習作業系統時,我們介紹了三個抽象:檔案是對 I/O 裝置的抽象虛擬記憶體是對程式儲存器的抽象,而程序是對一個正在執行的程式的抽象。我們再增加一個新的抽象:虛擬機器,它提供對整個計算機的抽象,包括作業系統、處理器和程式。

(第一章完~~)