深刻理解計算機系統系列【計算機系統漫遊】

操做系統原理是計算機行業基本功,想要成爲一名計算機領域的專業人士,必不可少要打好基礎。最近打算重點讀一讀《深刻理解計算機系統》這本書,回顧和提高本身對計算機和操做系統的理解。這是第一篇:【計算機系統漫遊】。【計算機系統漫遊】主要經過跟蹤hello程序的生命週期來開始對系統的學習----從它被程序員建立開始,到在系統上運行,輸出簡單的消息,而後終止。本文將沿着這個程序的生命週期,簡要地介紹一些逐步出現的關鍵概念、專業術語和組成部分。linux

1 信息就是【位+上下文】

首先咱們來看hello.c源程序:程序員

1 #include <stdio.h>
2 
3 int main() 4 { 5     printf("hello world!\n"); 6     return 0; 7 }

hello.c源程序實際上就是一個由值0和1組成的位(又稱爲比特)序列,8個位被組織成一組,稱爲字節。每一個字節表示程序中的某些文本字符。過去大部分的計算機系統都使用ASCII標準(如今有gbk/Unicode/utf8)來表示文本字符,這種方式實際上就是用 一個惟一的單字節大小的整數值來表示每一個字符。下圖中給出了 hello .c 程序的ASCII碼錶示。shell

根據上圖,咱們須要明白如下幾個原理或概念:
(1)hello.c 程序是以字節序列的方式儲存在文件中的。
(2)每一個字節都有一個整數值,對應某些字符。
(3)文本文件:像 hello.c 這樣只由ASCII字符構成的文件;全部其餘文件都稱爲二進制文件。
(4)系統中全部的信息,包括磁盤文件、內存中的程序、內存中存放的用戶數據以及網絡上傳送的數據,都是由一串比特表示的。區分不一樣數據對象的惟一方法是咱們讀到這些數據對象時的上下文。好比,在不一樣的上下文中 ,一個一樣的字節序列可能表示一個整數、浮點數、字符串或者機器指令。數組

2 程序被其餘程序翻譯成不一樣的格式

hello.c 程序的生命週期是從一個高級C語言程序開始的,由於這種形式可以被人讀懂。可是,爲了在系統上運行hello.c 程序,每條C語句都必須被其餘程序轉化爲一系列的低級機器語言指令。而後這些指令按照一種稱爲可執行目標程序(可執行目標文件)的格式打好包,並以二進制磁盤文件的形式存放起來。
在 Unix系統上,從源文件到目標文件的轉化是由編譯器驅動程序完成的,具體命令是: linux> gcc -o hello hello.c。此處,GCC編譯器驅動程序讀取源程序文件hello.c ,並把它翻譯成一個可執行目標文件hello 。這個翻譯過程可分爲四個階段完成,以下圖所示。執行這四個階段的程序(預處理器、編譯器、彙編器和連接器)一塊兒構成了編譯系統(compilation system )。緩存

理解編譯系統的四個階段:
(1)預處理階段:讀取系統頭文件stdio.h的內容,並把它直接插人程序文本中,獲得另外一個.i結尾的c程序。
(2)編譯階段:將文本文件hello.i 翻譯成彙編語言,獲得文本文件hello.s。彙編語言爲不一樣高級語言的不一樣編譯器提供了通用的輸出語言,且每條彙編語句以一種文本格式描述了一條低級機器語言指令。
(3)彙編階段:將hello.s翻譯成機器語言指令,把這些指令打包成一種叫作可重定位目標程序(relocatable object program)的格式,並將結果保存在目標文件hello.o中。hello.o文件是一個二進制文件,它包含的17個字節是函數main 的指令編碼。
(4)連接階段:將C編譯器提供的標準C庫中printf函數所在的目標文件(庫文件)printf.o以某種方式合併到hello.o程序中,獲得二進制可執行文件hello。該二進制文件能夠被加載到內存中,由系統執行。安全

3 瞭解編譯系統工做原理的益處

(1)優化程序性能。
(2)理解連接時出現的錯誤。
(3)避免安全漏洞。
這些益處在後面的文章中會具體闡述。bash

4 處理器讀並解釋儲存在內存中的指令

到這裏,hello.c 源程序已經被編譯系統翻譯成了可執行目標文件hello ,並被存放在磁盤上。要想在Unix系統上運行該可執行文件,咱們將它的文件名輸入到稱爲shell的應用程序中:網絡

linux> ./hello 
hello, world!
linux>

shell是一個命令行解釋器,它輸出一個提示符,等待輸人一個命令行,而後執行這個命令。若是該命令行的第一個單詞不是一個內置的shell命令,那麼shell就會假設這是一個可執行文件的名字,它將加載並運行這個文件。因此在此例中,shell將加載並運行 hello 程序,而後等待程序終止。hello 程序在屏幕上輸出它的信息,而後終止。shell隨後輸出一個提示符,等待下一個輸人的命令行。數據結構

5 系統的硬件組成

爲了理解運行hello程序時發生了什麼,咱們須要瞭解一個典型系統的硬件組成,以下圖所示。架構

(1)總線

貫穿整個系統的是一組電子管道,稱做總線,它攜帶信息字節並負責在各個部件間傳遞。一般總線被設計成傳送定長的字節塊,也就是字(word)。字中的字節數(即字長)是一個基本的系統參數,各個系統中都不盡相同。如今的大多數機器字長要麼是4個字節(32 位),要麼是8個字節(64位)。本書中,咱們不對字長作任何固定的假設。相反,咱們將在須要明肯定義的上下文中具體說明一個「字」是多大 。

(2)I/O設備

I/O(輸人/輸出)設備是系統與外部世界的聯繫通道。示例系統包括四個I/O設備 :做爲用戶輸人的鍵盤鼠標,做爲用戶輸出的顯示器,以及用於長期存儲數據和程序的磁盤驅動器(簡單地說就是磁盤)。
最開始,可執行程序hello就存放在磁盤上。 每一個I/O設備都經過一個控制器或適配器與I/O總線相連。控制器和適配器之間的區別主要在於它們的封裝方式。控制器是I/O設備自己或者系統的主印製電路板(一般稱做主板)上的芯片組;而適配器則是一塊插在主板插槽上的卡。它們的功能都是在I/O總線和I/O設備之間傳遞信息。

(3)主存

主存是一個在處理器執行程序時用來臨時存放程序和程序處理的數據的存儲設備
從物理上來講,主存是由一組動態隨機存取存儲器(DRAM)芯片組成的。從邏輯上來講,存儲器是一個線性的字節數組,每一個字節都有其惟一的地址(數組索引),且地址是從零開始的。
組成程序的每條機器指令都由不一樣數量的字節構成。與C程序變量相對應的數據項的大小是根據類型變化的。好比,在運行Linux的X86_64機器上,short類型的數據須要2個字節,int和 float類型須要4個字節,而long和double類型須要8個字節。

(4)處理器

中央處理單元(CPU ),簡稱處理器,是解釋(或執行)存儲在主存中指令的引擎。處理器的核心是一個大小爲一個字的存儲設備(或寄存器),稱爲程序計數器(PC)。在任什麼時候刻 ,PC都指向主存中的某條機器語言指令(即含有該條指令的地址)。

從系統通電開始,直到系統斷電,處理器一直在不斷地執行程序計數器指向的指令,再更新程序計數器使其指向下一條指令。處理器看上去是按照一個很是簡單的指令執行模型來操做的,這個模型是由指令集架構決定的。
在這個模型中,指令按照嚴格的順序執行,而執行一條指令包含執行一系列的步驟。處理器從程序計數器指向的內存處讀取指令,解釋指令中的位,執行該指令指示的簡單操做,而後更新PC,使其指向下一條指令,而這條指令並不必定和在內存中剛剛執行的指令相鄰。
這樣的簡單操做並很少,它們圍繞着主存、寄存器文件(register file)和算術/邏輯單元(ALU)進行。寄存器文件是一個小的存儲設備,由一些單個字長的寄存器組成,每一個寄存器都有惟一的名字。ALU計算新的數據和地址值。下面是一些簡單操做的例子, CPU在指令的要求下可能會執行這些操做。
        加載:從主存複製一個字節或者一個字到寄存器,以覆蓋寄存器原來的內容。
        存儲:從寄存器複製一個字節或者一個字到主存的某個位置,以覆蓋這個位置上原來的內容。
        操做:把兩個寄存器的內容複製到ALU, ALU對這兩個字作算術運算,並將結果存放到一個寄存器中,以覆蓋該寄存器中原來的內容。
        跳轉:從指令自己中抽取一個字,並將這個字複製到程序計數器(PC)中,以覆蓋PC中原來的值。
處理器看上去是它的指令集架構的簡單實現,可是實際上現代處理器使用了很是複雜的機制來加速程序的執行。所以,咱們將處理器的指令集架構和處理器的微體系結構區分開來:指令集架構描述的是每條機器代碼指令的效果;而微體系結構描述的是處理器其實是如何實現的。

6 運行hello程序

主要分兩個階段。

(1)第一階段從鍵盤讀取hello命令

首先,當咱們在鍵盤上輸入字符串 「./hello」 後 ,shell程序將字符逐一讀人寄存器,再把它存放到內存中。
其次,在鍵盤上敲回車鍵時,shell程序就知道咱們已經結束了命令的輸人。而後shell執行一系列指令來加載可執行的hello文件,這些指令將hello目標文件中代碼數據從磁盤複製到主存。數據包括最終會被輸出的字符串"hello, World\n!"。以下圖《從鍵盤讀取hello命令》:

補充:直接存儲器存取(DMA)技術,數據能夠不經過處理器而直接從磁盤到達主存。以下圖:

(2)第二階段將輸出字符串從存儲器寫到顯示器
一旦目標文件hello中的代碼和數據被加載到主存,處理器就開始執行hello程序的 main程序中的機器語言指令。
這些指令將"hello, world!"字符串中的字節從主存複製到寄存器文件,再從寄存器文件中複製到顯示設備,最終顯示在屏幕上。以下圖:

7 高速緩存

這個簡單的示例揭示了一個重要問題:即系統花費了大量的時間把信息從一個地方複製到另外一個地方

從程序員的角度來看,這些複製就是開銷,減慢了程序「真正」的工做。所以,系統設計者的一個主要目標就是使這些複製操做盡量快地完成。
根據機械原理,較大的存儲設備要比較小的存儲設備運行得慢,而快速設備的造價遠高於同類的低速設備。然而,處理器從寄存器文件中讀數據比從主存中讀取幾乎要快100倍,且這種處理器與主存之間的差距還在持續增大。加快處理器的運行速度比加快主存的運行速度要容易和便宜得多。
針對這種處理器與主存之間的差別,系統設計者採用了更小更快的存儲設備,稱爲高速緩存存儲器(cache m em ory,簡稱爲cache或高速緩存),做爲暫時的集結區域,存放處理器近期可能會須要的信息。位於處理器芯片上的L1高速緩存的容量能夠達到數萬字節,訪問速度幾乎和訪問寄存器文件同樣快。現代計算機一般採用三級高速緩存:Ll、L2和L3。系統能夠得到一個很大的存儲器,同時訪問速度也很快,緣由是利用了高速緩存的局部性原理,即程序具備訪問局部區域裏的數據和代碼的趨勢。經過讓高速緩存裏存放可能常常訪問的數據,大部分的內存操做都能在快速的高速緩存中完成。高速緩存存儲器示意圖以下:

接下來咱們看一下存儲器層次結構,先看示意圖:

從上至下,設備的訪問速度愈來愈慢、容量愈來愈大,而且每字節的造價也愈來愈便宜。寄存器文件在層次結構中位於最頂部,也就是第0 級或記爲L0。這裏咱們展現的是三層高速緩存L1到L3, 佔據存儲器層次結構的第1層到第3層。

存儲器層次結構的主要思想是上一層的存儲器做爲低一層存儲器的高速緩存。所以,寄存器文件就是L1的高速緩存,L1是L2的高速緩存,L2是L3的高速緩存,L3是主存的高速緩存,而主存又是磁盤的高速緩存。

8 操做系統管理硬件

當shell加載和運行hello程序時,以及hello程序輸出本身的消息時,shell和hello程序都沒有直接訪問鍵盤、顯示器、磁盤或者主存,而是依靠操做系統提供的服務間接訪問硬件的。
操做系統能夠當作是應用程序和硬件之間插人的一層軟件,全部應用程序對硬件的操做嘗試都必須經過操做系統。以下圖所示。

操做系統有兩個基本功能
(1)防止硬件被失控的應用程序濫用
(2)嚮應用程序提供簡單一致的機制控制複雜而又一般大不相同的低級硬件設備

操做系統經過幾個基本的抽象概念(進程、虛擬內存和文件)來實現這兩個功能。
(1)文件:對I/O設備的抽象表示
(2)虛擬內存:對主存和磁盤I/O設備的抽象表示
(3)進程:對處理器、主存和I/O設備的抽象表示

8.1 進程

進程是操做系統對一個正在運行的程序的一種抽象。在一個系統上能夠同時運行多個進程,而每一個進程都好像在獨佔地使用硬件。而併發運行,則是說一個進程的指令和另外一個進程的指令是交錯執行的。
操做系統保持跟蹤進程運行所需的全部狀態信息,這種狀態就是上下文。上下文包括許多信息:好比PC和寄存器文件的當前值,以及主存的內容
在任何一個時刻,單處理器系統都只能執行一個進程的代碼。當操做系統決定要把控制權從當前進程轉移到某個新進程時,就會進行上下文切換,即保存當前進程的上下文、恢復新進程的上下文,而後將控制權傳遞到新進程。新進程就會從它上次中止的地方開始。下圖展現了示例hello程序運行場景的基本理念。

注意,從一個進程到另外一個進程的轉換是由操做系統內核(kernel)管理的。內核是操做系統代碼常駐主存的部分。當應用程序須要操做系統的某些操做時,好比讀寫文件,它就執行一條特殊的系統調用(system call)指令,將控制權傳遞給內核。而後內核執行被請求的操做並返回應用程序。內核不是一個獨立的進程,它是系統管理所有進程所用代碼和數據結構的集合

8.2 虛擬內存

虛擬內存是一個抽象概念,它爲每一個進程提供了一個假象,即每一個進程都在獨佔地使用主存。每一個進程看到的內存都是一致的,稱爲虛擬地址空間。下圖所示的是Linux進程的虛擬地址空間(其餘Unix系統的設計也與此相似)。請注意,圖中的地址是從下往上增大的。
在Linux中,
地址空間最上面的區域:是保留給操做系統中的代碼和數據的,這對全部進程來講都同樣
地址空間的底部區域:存放用戶進程定義的代碼數據

每一個進程看到的虛擬地址空間由大量準肯定義的區構成,每一個區都有專門的功能。此處,先簡單瞭解每個區,對後續的詳細學習是很是有益的。咱們從最低的地址開始,逐步向上介紹
(1)程序代碼和數據
對全部的進程來講,代碼是從同一固定地址開始,緊接着的是和C全局變量相對應的數據位置。代碼和數據區是直接按照可執行目標文件的內容初始化的,在示例中就是可執行文件hello 。後續研究連接和加載時,會學習更多有關地址空間的內容。
(2)堆
代碼和數據區後緊隨着的是運行時堆代碼和數據區在進程一開始運行時就被指定了大小,與此不一樣,當調用像malloc和free這樣的C標準庫函數時,堆能夠在運行時動態地擴展和收縮。後續學習管理虛擬內存時,咱們將更詳細地研究堆。
(3)共享庫
大約在地址空間的中間部分是一塊用來存放像C標準庫和數學庫這樣的共享庫的代碼和數據的區域。共享庫的概念很是強大,也至關難懂。後續學習動態連接時,將學習共享庫是如何工做的。
(4)棧
位於用戶虛擬地址空間頂部的是用戶棧,編譯器用它來實現函數調用。和堆同樣,用戶棧在程序執行期間能夠動態地擴展和收縮。特別地,每次咱們調用一個函數時,棧就會增加;從一個函數返回時,棧就會收縮。
(5)內核虛擬內存
地址空間頂部的區域是爲內核保留的不容許應用程序讀寫這個區域的內容或者直接調用內核代碼定義的函數。相反,它們必須調用內核來執行這些操做。

虛擬內存的運做須要硬件和操做系統軟件之間精密複雜的交互,包括對處理器生成的每一個地址的硬件翻譯。基本思想是把一個進程虛擬內存的內容存儲在磁盤上,而後用主存做爲磁盤的高速緩存

8.3 文件

文件就是字節序列,僅此而已。每 個 I/O 設備,包括磁盤、鍵盤、顯示器,甚至網絡,均可以當作是文件。系統中的全部輸入輸出都是經過使用一小組稱爲Unix I/O的系統函數調用讀寫文件來實現的。
文件這個簡單而精緻的概念是很是強大的,由於它嚮應用程序提供了一個統一的視圖,來看待系統中可能含有的全部各式各樣的I/O設備。例如,處理磁盤文件內容的應用程序員能夠很是幸福,由於他們無須瞭解具體的磁盤技術。進一步說,同一個程序能夠在使用不一樣磁盤技術的不一樣系統上運行。

9 Amdahl 定律

計算領域的早期先鋒之一Gene Amdahl,對提高系統某一部分性能所帶來的效果作出了簡單卻有見地的觀察。這個觀察被稱爲Amdahl定律(Amdahl’s law)。
該定律的主要思想是,當咱們對系統的某個部分加速時,其對系統總體性能的影響取決於該部分的重要性和加速程度。
若系統執行某應用程序須要時間爲Told。假設系統某部分所需執行時間與該時間的比例爲α,而該部分性能提高比例爲k。即該部分初始所需時間爲αTold,如今所需時間爲(αTold)/k。所以 ,總的執行時間應爲:

由此,能夠計算加速比 S=Told/Tnew 爲:

考慮這樣一種狀況,系統的某個部分初始耗時比例爲60%(α=0.6),其加速比例因子爲3(k=3)。則咱們能夠得到的加速比爲1/[0.4+0.6/3] = 1.67倍。雖然咱們對系統的一個主要部分作出了重大改進,可是得到的系統加速比卻明顯小於這部分的加速比。

這就是Amdahl定律的主要觀點—— 要想顯著加速整個系統,必須提高全系統中至關大的部分的速度

10 併發和並行

計算機的整個歷史中,有兩個需求是驅動進步的持續動力:
(1)想要計算機作得更多;(2)想要計算機運行得更快。 
當處理器可以同時作更多的事情時,咱們用的術語併發(concurrency)是一個通用的概念,指一個同時具備多個活動的系統;而術語並行(parallelism)指的是用併發來使一個系統運行得更快。
併發:交錯執行。
並行:同時執行。
「並行」概念是「併發」概念的一個子集。

併發是兩個隊列交替使用一臺咖啡機;並行是兩個隊列同時使用兩臺咖啡機;若是串行,一個隊列使用一臺咖啡機,那麼哪怕前面那我的便祕了去廁所呆半天,後面的人也只能死等着他回來才能去接咖啡,這效率無疑是最低的。

11 小結

(1)計算機系統是由硬件和系統軟件組成的,它們共同協做以運行應用程序。

(2)計算機內部的信息被表示爲一組組的位,它們依據上下文有不一樣的解釋方式。

(3)程序被其餘程序翻譯成不一樣的形式,開始時是ASCII文本(或者其餘編碼的文本),而後被編譯器和連接器翻譯成二進制可執行文件。

(4)編譯系統的四個階段:預處理,編譯,彙編,連接。

(5)處理器讀取並解釋存放在主存裏的二進制指令。

(6)計算機主要硬件:總線,主存(內存),IO設備,處理器。

(7)由於計算機花費了大量的時間在內存、I/O設備和CPU寄存器之間複製數據,因此將系統中的存儲設備劃分紅層次結構:CPU寄存器在頂部,接着是多層的硬件髙速緩存存儲器、DRAM主存和磁盤存儲器。

(8)在層次模型中,位於更高層的存儲設備比低層的存儲設備要更快,單位比特造價也更高。

(9)層次結構中較高層次的存儲設備能夠做爲較低層次設備的高速緩存。經過理解和運用這種存儲層次結構的知識,程序員能夠優化C程序的性能。

(10)操做系統內核是應用程序和硬件之間的媒介。它提供三個基本的抽象:
  1) 文件是對I/O 設備的抽象;文件自己就是字節序列。 
  2) 虛擬內存是對主存和磁盤的抽象;虛擬內存提供的抽象讓每一個進程看到的內存都是一致的,稱爲虛擬地址空間。
  3) 進程是處理器、主存和I/O設備的抽象。

(11)在虛擬地址空間中,從下往上(從低地址到高地址)的順序是:程序代碼和數據、堆、共享庫、棧、內核虛擬地址空間。

(12)進程是操做系統對一個正在運行的程序的一種抽象。上下文就是操做系統跟蹤進程運行所需的全部狀態。進程的切換就是須要進行上下文切換:即保存當前進程的上下文、恢復新進程的上下文,而後將控制權傳遞到新進程

(13)網絡提供了計算機系統之間通訊的手段。從特殊系統的角度來看,網絡就是一種I/O設備。

相關文章
相關標籤/搜索