天天看點

CSAPP 1 - 計算機系統漫遊

CS:APP —— Computer Systems: A Programmer's Perspective, 中譯本名為: 《深入了解計算機系統》.

CS:APP是從 **程式員的角度** 出發, 講述應用程式員如何能夠利用系統知識來編寫出更好的程式, 涉及到系統的硬體架構、作業系統、編譯器、網絡等基礎方面.

目錄

  • 0 序言及摘要
  • 1 資訊就是位+上下文
  • 2 程式被其他程式翻譯成不同的格式
  • 3 了解編譯系統如何工作是大有益處的

(1) 序言:

從書名可以得知:

大部分系統書籍都是從 建構者的角度 來寫, 講述如何實作硬體或系統軟體, 包括作業系統、編譯器和網絡接口;

而 CS:APP 是從 程式員的角度 出發, 講述應用程式員如何能夠利用系統知識來編寫出更好的程式, 涉及到系統的硬體架構、作業系統、編譯器、網絡等基礎方面.

(2) 摘要:

計算機系統是由一個硬體和系統軟體組成的, 它們共同協作以運作應用程式. 計算機内部的資訊被表示為一組組的位, 他們依據上下文有不同的解釋方式. 程式被其他程式翻譯成不同的形式, 開始時是ASCII文本, 然後被編譯器和連結器翻譯成二進制可執行檔案.

處理器讀取并解釋存放在主存裡的二進制指令. 因為計算機花費了大量的時間在記憶體、I/O裝置和CPU寄存器之間複制資料, 是以将系統中的儲存設備劃分成層次結構 —— CPU寄存器在頂部, 接着是多層的硬體高速緩存存儲器、DRAM主存和磁盤存儲器.

更高層的儲存設備比低層的儲存設備讀寫更快、機關比特造價也更高.

較高層次的儲存設備可以作為較低層次裝置的高速緩存.

作業系統核心是應用程式和硬體之間的媒介, 它提供三個基本的抽象:

a. 檔案是對I/O裝置的抽象;

b. 虛拟記憶體是對貯存和磁盤的抽象;

c. 程序是處理器、主存和I/O裝置的抽象.

網絡提供了計算機系統之間通信的手段 —— 可以把網絡當作是一種I/O裝置.

以下面的C程式

hello.c

為例:

#include <stdio.h>
int main() {
    printf("hello, world\n");
    return 0;
}
           

源程式實際上就是一個由0、1組成的位(bit, 比特)序列, 8個位被組成一組, 就是一個位元組(Byte). 每個位元組表示程式中的某些文本字元.

大部分現代計算機系統都适用ASCII标準來表示文本字元 —— 實際上就是用一個唯一的單位元組大小的整數值來表示每個字元, 比如

#由35表示, i由105表示

.

hello.c程式是以 位元組序列 的方式儲存在檔案中的, 每個位元組都有一個整數值, 對應某些特定的字元.

注意: 每一行都是以一個看不見的換行符

\n

結束的, 對應的ASCII碼是10.

hello.c的表示方法說明一個基本思想:

系統中的所有資訊——包括磁盤檔案、記憶體中的程式、記憶體中存放的使用者資料, 以及網絡上傳輸的資料, 都是由一串比特表示的.
區分不同資料對象的唯一方法就是我們讀到這些資料對象時的上下文.
           
讀書筆記: 上下文: 可以類比文章的前後文, 一個單獨的int, 我們并不能明确地知道它的實際作用, 隻有給出相鄰的比特, 我們才能确定它的具體含義. 比如hello.c中, 由int的上下文可以知道, 這裡的int是指明目前函數的傳回值類型為int(整型).

像上面的hello.c是我們開發人員可以看懂的進階語言編寫的源代碼檔案, 要在系統上運作, 就必須通過其他程式将其轉化為一系列的低級 機器語言 指令, 然後再将這些指令大包圍 可執行目标程式(目标檔案), 并以二進制磁盤檔案的形式存放起來.

在Unix系統上, 從源檔案到目标檔案的轉化是由 編譯器驅動程式 完成的:

gcc hello.c -o hello
           

這裡通過GCC編譯器驅動程式讀取源程式檔案hello.c, 并把它變異成一個可執行目标檔案hello, 具體步驟為:

(1) 預處理階段: 預處理器(cpp)根據以字元#開頭的指令, 修改原始的C程式: 這裡将讀取引入的系統頭檔案stdio.h, 将它插入到源程式中文本, 得到另一個C程式, 一般為hello.i;

(2) 編譯階段: 編譯器(ccl)将文本檔案hello.i翻譯成文本檔案hello.s, 就是一個彙編語言程式;

(3) 彙編階段: 彙編器(as)将hello.s翻譯成機器語言指令, 把這些指令打包成一種叫做 可重定位目标程式 的格式, 并将結果儲存在目标檔案hello.o中 —— 是一個二進制檔案;

(4) 連結階段: hello.c中調用了

printf

列印函數, 這個函數是每個C編譯器都提供的 标準C庫 中的, 存在于printf.o的預編譯好了的目标檔案中, 在此階段由連結器(ld)将printf.o合并到hello.o中, 最後得到hello檔案 —— 可執行目标檔案, 可以被加載到記憶體中由系統執行.

(1) 優化程式性能

比如:

一個switch語句是否總是比if-else語句高效?

一個函數調用的開銷有多大? while循環比for循環高效嗎?

指針引用比數組索引更有效嗎?

為什麼将循環求和的結果放到一個本地變量中, 會比将其放到一個通過引用傳遞過來的參數中, 運作要快很多呢?

(2) 了解連結時出現的錯誤

一些最令人困擾的、難以定位的程式錯誤往往都和連結器操作有關. 比如:

連結器報告說無法解析一個引用是什麼意思?

靜态變量和全局變量的差別是什麼?

靜态庫和動态庫的差別是什麼?

指令行上排列庫的順序有什麼影響?

(3) 避免安全漏洞

緩沖區溢出錯誤是造成大多數網絡和Internet伺服器上安全漏洞的主要原因 —— 因為很少有程式開發人員能夠了解 需要限制從不受信任的源接收資料的數量和格式.

安全程式設計的第一步就是了解 資料和控制資訊存儲在程式棧上的方式會引起的後果.

未完待續...

繼續閱讀