文章同步發於 github 倉庫和 csdn 博客。因為文章會持續優化,所以最新版以 github 倉庫為準。
1,信息就是位+上下文
計算機系統是由硬件和系統軟件組成,它們共同工作來運行應用程序。
-
C 語言是系統級編程的首選,同時它也非常實用於應用級程序的編寫。
2,程序被其他程序翻譯成不同格式
以hello
程序為例來解釋程序的生命週期,hello.c
文件代碼如下:
#include <stdio.h>int main(){printf("hello world\n");return 0;}
為了在系統上運行 hello.c
程序,文件中的每條 C 語言都必須被其他程序轉化為一系列的低級機器語言指令。然後這些指令按照一種稱為可執行目標程序的格式打包好,並以二進制磁盤文件的形式保存。目標程序也稱為可執行目標文件。
在 Linux 系統中,GCC
編譯器將源程序文件 hello.c 翻譯(編譯)為目標文件 hello 的過程分為四個階段,如下圖所示。執行這四個階段(預處理器、編譯器、彙編器和鏈接器)的程序一起構成了編譯器系統(compilation system)。
3,瞭解編譯器如何工作是有大有益處的
優化程序性能。瞭解一些機器代碼(彙編代碼)以及編譯器將不同的 C/C++語句轉化為機器代碼的方式。比如一個函數調用的開銷有多大?while 循環比 for 循環更有效嗎?指針引用比數組索引更有效嗎等?
-
理解鏈接時出現的錯誤。比如靜態庫和動態庫的區別,靜態變量和全局變量的區別等。
-
避免安全漏洞。
4,處理器讀取存在內存中的指令
典型系統的硬件組織構成: 總線、I/O
設備、主存(動態隨機存取存取器 DRAM
)、處理器。
主存是臨時存儲設備,在處理器執行程序時,用來存放程序和程序處理的數據。從邏輯上說,存儲器是一個線性的字節數組,每個字節都有其唯一的地址(數組索引),這些地址是從零開始的。
將 hello 程序輸出的字符串從存儲器寫到顯示器的過程如下圖所示。
5,高速緩衝至關重要
針對處理器與主存之間讀取數據的差異,處理器系統設計者採用了更小更快的存儲設備,稱為高速緩衝器(cache memory,簡稱 cache 或高速緩衝),作為暫時的集結區域,存放處理器近期可能會需要的信息。
6,存儲設備形成層次結構
在處理器和一個較大較慢的設備(例如主存)之間插入一個更小更快的存儲設備(例如高速緩衝)的想法已經成為一個普遍的觀念。實際上,每個計算機系統中的存儲設備都被組織成了一個存儲器層次結構,如圖1-9所示。在這個層次結構中,從上至下,設備的訪問速度越來越慢、容量越來越大,並且每字節的成本也越來越低。
7,操作系統管理硬件
所有應用程序對硬件的操作嘗試都必須通過操作系統,操作系統有兩個基本功能:
-
防止硬件被失控的應用程序濫用;
-
嚮應用程序提供簡單一致的機制來控制複雜而又通常大不相同的低級硬件設備。
操作系統通過幾個基本的抽象概念(進程、虛擬內存和文件)來實現這兩個功能。文件是對 I/O 設備的抽象表示,虛擬內存是對主存和磁盤 I/O 設備的抽象表示,進程則是對處理器、主存和 I/O 設備的抽象表示。
7.1,進程
進程是操作系統對一個正在運行的程序的一種抽象。在一個系統上可以同時運行多個進程,每個進程都好像在獨佔地使用硬件。而併發運行,則是說一個進程的指令和另一個進程的指令是交錯之行的。在大多數系統中,需要運行的進程數是可以多於它們的 CPU
個數的。無論在單核還是多核系統中,一個 CPU
看上去都像是在併發地執行多個進程,這實際是通過處理器在進程間切換來實現的,操作系統實現這種交錯執行的機制成為上下文切換。
操作系統會保持跟蹤進程運行所需的所有狀態信息,這種狀態信息稱為上下文,其包括多種信息,比如 PC
和寄存器文件的當前值,以及主存的內容。在任何一個時刻,但處理器系統都只能執行一個進程的代碼。當操作系統決定要把控制權從當前進程轉移到某個新進程時,就會進行上下文切換,即保存當前進程的上下文、恢復進程的上下文,然後將控制權傳遞到新進程。圖1-12展示了示例 hello 程序運行場景的基本理念。
7.2,線程
儘管我們直觀認為一個進程只有單一的控制流,但在現代計算機系統中,一個進程實際上可以由多個稱為線程的執行單元組成,每個線程都運行在進程的上下文中,並共享同樣的代碼和全局數據。由於網絡服務器對並行處理的需求,線程成為越來越重要的編程模型,因為多線程之間比多進程之間更容易共享數據且更高效。
7.3,虛擬內存
虛擬內存是一個抽象概念,它為每個進程提供了一個假象,即每個進程都在獨佔地使用主存,每個進程看到的內存都是一致的,稱為虛擬地址空間。圖 1-13 所示的是 Linux 進程的虛擬地址空間。在 Linux 中,地址空間最上面的區域是保留給操作系統中代碼和數據的,這對所有進程來說都是一樣。地址空間的底部區域存放用戶進程定義的代碼和數據。注意,圖中的地址從下往上增大的。
每個進程看到的虛擬地址空間由大量準確定義的區構成,每個區都有專門的功能,進程的虛擬地址空間意義如下。
-
程序代碼和數據。對所有進程來說,代碼是從同一固定地址開始,進接著的是和全局變量和相對應的數據位置,代碼和數據區是直接按照可執行目標文件的內容初始化的。
-
堆。代碼和數據區後緊隨著的是運行時堆。代碼和數據區在進程一開始運行時就被指定了大小,與此不同,當調用
malloc
和free
這樣的 C 標準庫函數時,堆可以在運行時動態地拓展和收縮。 -
共享庫。地址空間的中間部分是一塊用來存放像 C 標準庫和數學庫這樣的共享庫的代碼和數據的區域。
-
棧。位於用戶虛擬地址空間頂部的是用戶棧,編譯器用它來實現函數調用。和堆類似,用戶棧在程序執行期間可以動態地拓展和收縮,調用函數棧則增長,從一個函數返回,棧則收縮。
-
內核虛擬內存。地址空間頂部的區域是為操作系統內核保留的。
虛擬內存的運作需要硬件和操作系統軟件之間精密複雜的交互,包括對處理器生成的每個地址的硬件翻譯,基本思想是把一個進程虛擬內存的內容存在在磁盤上,然後用主存作為磁盤的高速緩衝。
7.4,文件
文件就是字節序列,每個 I/O 設備包括磁盤、鍵盤、顯示器,甚至網絡都可以看成文件,系統中所有輸入輸出都是通過使用一小組稱為 Unix I/O 的系統函數調用讀寫文件來實現的。
8,系統之間利用網絡通信
現代系統通過網絡將單個計算機系統連接在一起。
9,重要主題
9.1,Amdahl 定律
該定律的主要思想就是,當我們對系統的某個部分加速時,其對系統整體性能的影響取決於該部分的重要性和加速程度。
9.2,併發與並行
我們用術語併發(concurrency
)是一個通用的概念,指一個同時具有多個活動的系統;而術語並行(parallelism
)指的是用併發來使一個系統運行得更快。並行可以在計算機系統的多個抽象層次上運用,這裡按照系統層次結構中由高到低的順序描述三個層次。
1,線程級併發
超線程,有時稱為同時多線程(simultaneous multi-threading),是一項允許一個 CPU
執行多個控制流的技術。它涉及 CPU 某些硬件有多個備份,比如程序計數器和寄存器文件,而其他硬件部分只有一份,比如執行浮點算術運算的單元。常規的處理器需要大約 20000 個時鐘週期做不同線程間的轉換,而超線程的處理器可以在單個週期的基礎上決定要執行哪一個線程。
多處理器的使用可以從兩方面提高系統性能,首先,它減少了在執行多個任務時模擬併發的需要;其次,它可以使應用程序運行得更快,前提是程序以多線程方式編寫。
2,指令級並行
在較低的抽象層次上,現代處理器可以同時執行多條指令的屬性稱為指令級並行。如果處理器可以達到比一個週期一條指令更快的執行速率,就稱為超標量(super-scalar)處理器,大多數現代處理器都支持超標量操作。
3,單指令、多數據並行
在最低層次中,許多現代處理器擁有特殊的硬件,允許一條指令產生多個可以並行執行的操作,這種方式稱為單指令、多數據,即 SIMD
並行。
9.4,計算機系統中抽象的重要性
前面我們介紹了計算機系統使用的幾個抽象,如圖 1-18 所示,在處理器裡,指令集架構提供了對實際處理器的抽象。
操作系統中有四個抽象:文件是對 I/O 設備的抽象,虛擬內存時對程序存儲器的抽象,進程是對一個正在運行的程序的抽象,虛擬機提供對整個計算機的抽象,包括操作系統、處理器和程序。
參考資料
《深入理解操作系統第三版-第一章》