讀《程式是如何跑起來的》

weixin_34321977發表於2018-04-14

2018-04-05

前言

作業系統將底層的很多抽象的原理封裝成物件導向的、方便大眾理解和操作的圖形介面、這大大提高了計算機操作的便利性,然而,享受方便的同事也付出了代價,對於底層的瞭解越來越少,只會使用工具,卻無法明白工具的底層機制就無法創造出更好的工具。

225323-933909427dcdb0e9.jpg
image

第1章 對程式設計師來說CPU是什麼

熱身問題

問:

  1. 程式是什麼?
  2. 程式是由什麼組成的?
  3. 什麼是機器語言?
  4. 正在執行的程式儲存在什麼位置?
  5. 什麼是記憶體地址?
  6. 計算機的構成元件中,負責程式的解釋和執行的是哪個?

答:

  1. 指示計算機每一步動作的一組指令
  2. 指令和資料
  3. CPU 可以直接識別並使用的語言
  4. 記憶體
  5. 記憶體中,用來表示命令和資料儲存位置的數值
  6. CPU

解析

  1. 一般所說的程式,譬如運動會、音樂會的程式等,指的是“行事的先後次序”。計算機程式也是一樣的道理。
  2. 程式是指令和資料的組合體。例如,C 語言“printf(" 你好");”這個簡單的程式中,printf是指令," 你好" 是資料。
  3. CPU 能夠直接識別和執行的只有機器語言。使用C、Java 等語言編寫的程式,最後都會轉化成機器語言。
  4. 硬碟和磁碟等媒介上儲存的程式被複制到記憶體後才能執行。
  5. 記憶體中儲存命令和資料的場所,通過地址來標記和指定。地址由整數值表示。
  6. 計算機的構成元件中,根據程式的指令來進行資料運算,並控制整個計算機的裝置稱作CPU。大家熟知的奔騰( Pentium )就是CPU 的一種。

1.1 CPU內部結構

積體電路 = CPU + 記憶體

  • CPU

    • 暫存器: 暫存指令、資料, 一般一個CPU包含20~100個暫存器.
    • 控制器:負責把記憶體上指令、資料的讀入暫存器.
    • 運算器: 負責運算,從記憶體讀入暫存器的資料。
    • 時鐘:發出CPU開始計時的時鐘訊號。
    225323-4d02434a9238c298.jpg
    image

程式啟動後,根據時鐘訊號,控制器會從記憶體中讀取指令和資料。通過對這些指令加以解釋和執行,運算器就會對資料進行運算。控制器根據該運算結果來控制(主要是資料輸人輸出的時機控制)計算機。

225323-ee72d8cc808854f8.jpg
image

小總結

程式編寫好後,被計算機複製到記憶體中,然後CPU的控制器從記憶體中讀取指令和資料,運算器對資料進行運算,最後控制器根據運算器的結果來控制計算器。因為現在計算機CPU的計算能力很強,所以這個過程很快。

暫存器

暫存器的主要種類和功能

225323-08189d45305d7165.jpg
image

表1-1 暫存器的主要種類和功能

種類 功能
程式計數器( program counter ) 儲存下一條指令所在記憶體的地址
標誌暫存器( flag register ) 儲存運算處理後的CPU 的狀態

組合語言

彙編:將組合語言編寫的程式轉化成機器語言的過程稱為彙編。
反彙編:將機器語言轉化成組合語言程式的過程稱為反彙編。

組合語言:組合語言採用助記符(memonic )來編寫程式,每一個原本是電氣訊號的機器語言“指令都會有一個與其相應的助記符,組合語言和機器語言基本上是一一對應的。

條件分支和迴圈機制

程式的流程
* 順序執行: 按照地址內容的順序執行指令
* 條件分支: 根據條件執行任意地址的指令
* 迴圈: 重複執行同一地址的指令

函式的呼叫機制

225323-97e5db1c1e58fa9c.jpg
image

函式實現了程式碼指令在記憶體的離散分佈。

  • Call指令: 呼叫函式後需要執行的質量地址存在棧中

  • Return指令: 將儲存在棧中的地址設定到程式計數器中,函式處理完後,下一個指令就會被讀取出來,再被設定到程式計數器中。

  • 棧(stack) 本來是“乾草等堆積如山”的意思。在程式領域中,通常使用
    該詞來表示不斷地儲存各種資料的記憶體區域。函式呼叫後之所以能正確
    地返回撥用前的地址,就是棧的功勞。

  • 陣列: 是指同樣長度的資料, 在記憶體中進行連續排列的資料構造。一個陣列名來表示全體資料,通過索引來區分陣列的各個資料(元素)。

  • 位:1位代表二進位制數的一個位元組位。

綜合使用地址和索引來決定實際地址
225323-0e8af8b7b3bf5d95.jpg
image

機器語言

225323-4924bf290ff25de0.jpg
image

原來CPU可以進行的處理非常少。雖然高階程式語言編寫的程式看起來非常複雜,但CPU實際處理的事情就是這麼簡單。

一點感想

機器語言指令必須要有讀寫和傳送的操作,還有邏輯運算,而跳轉和Call、Return指令是高階一點的指令。

感覺CPU其實主要發揮它的效能優勢,計算速度快,但是專注於快速處理資料運算,CPU無法處理複雜的高階邏輯,所以才出現了各種方便程式設計師理解的高階程式語言。反應到生活中,人也要儘量發揮個人的長處,才能創造更大的價值吧,

第2章 資料是用二進位制表示的

  1. 32 位是幾個位元組?
  2. 二進位制數01011100 轉換成十進位制數是多少?
  3. 二進位制數00001111左移兩位後,會變成原數的幾倍?
  4. 補碼形式表示的8位二進位制數11111111,用十進位制數表示
    的話是多少?
  5. 補碼形式表示的8位二進位制數10101010,用16位的二進
    制數表示的話是多少?
  6. 反轉部分圖形模式時,使用的是什麼邏輯運算?

解析

  1. 因為8位=1位元組,所以32位就是32/8=4位元組。
  2. 92, 將二進位制數的各數位的值和位權相乘後再相加,即可轉換成十進位制數。
  3. 4倍, 二進位制數左移1位後會變成原來的值的2倍。左移兩位後,就是2倍的2倍,即4倍。
  4. -1, 所有位都是1的二進位制數,用十進位制數表示的話就是-1。
  5. 1111111110101010, 使用原數的最高位1來填充高位。
  6. XOR 運算只反轉與1相對應的位。NOT運算是反轉所有的位。

二進位制的IC

計算機處理資訊最小的單位是位——就相當於二進位制中的一位(binary digit)


225323-4aeec158f92c6cdc.jpg
image

位元組

二進位制數的位數一般是8位、16位、32位.....也就是8的倍數, 這是因為計算機所處理的資訊的基本單位是8位二進位制數。

8位二進位制數被稱為一個位元組。位元組是最基本的資訊計量單位

位是最小單位,位元組是基本單位。

記憶體和磁碟都使用位元組單位來儲存和讀寫資料,使用位單位則無法讀寫資料。因此,位元組是資訊的基本單位。

用位元組單位處理資料時,如果數字小於儲存資料的位元組數( 二進位制數的位數),那麼高位上就用0填補。例如,100111這個6 位二進位制數,用8位(=1位元組)表示時為00100111,用16位(=2位元組)表示時為000000000100111。

對於用二進位制數表示的資訊,計算機不會區分它是數值、文字,還是某種圖片的模式等,而是根據編寫程式的各位對計算機發出的指示來進行資訊的處理(運算)。具體進行何種處理,取決於程式的編寫方式。

位運算

十進位制數左移後會變成原來的10倍、100倍、1000倍.....樣,二進位制數左移後就會變成原來的2倍、4倍、8倍.....之二進位制數右移後則會變成原來的1/4、1./.....這樣一來,大家應該能夠理解為什麼移位運算能代替1/2、乘法運算和除法運算了吧。

二進位制數中最高位稱為符號位。符號位是0 時表示正數,符號位是1時表示負數。

225323-66cc665f13e6ebeb.jpg
image
225323-b794b36ae6058585.jpg
image

補數

  • 二進位制的補數為"取反+1"
  • 一個負數可用它的正補數來代替,而這個正補數可以用模加上負數本身來得到。
  • 一個正數和一個負數互為補數時,兩數的絕對值之和為模
  • 正數的補數為其自身。

邏輯右移

225323-34d9232165188cdc.jpg
image

算術右移

  • 將二進位制數作為帶符號的數值進行運算時,移位後要在最高位填充移位前符號位的值(0 或1)。

  • 如果數值是用補數表示的負數值,那麼右移後在空出來的最高位補1,就可以正確地實現1/2、1/4、1/8 等的數值運算。如果是正數,只需在最高位補0即可。

  • 只有在右移時才必須區分邏輯位移和算術位移。左移時, 只需在空出來的低位補0即可。

225323-db6165a62cbc9baf.jpg
image

第3章 計算機進行小數運算出錯的原因

問:

  1. 二進位制數0.1,用十進位制數表示的話是多少?
  2. 用小數點後有3 位的二進位制數,能表示十進位制數0.625 嗎?
  3. 將小數分為符號、尾數、基數、指數4 部分進行表現的形式
    稱為什麼?
  4. 二進位制數的基數是多少?
  5. 通過把0 作為數值範圍的中間值,從而在不使用符號位的情
    況下來表示負數的表示方法稱為什麼?
  6. 10101100.01010011這個二進位制數,用十六進位制數表示的
    話是多少?

答:

  1. 二進位制數的小數點後第一位的位權是2^-1= 0.5。也就是說,二進位制數0.1—> 1 x 0.5 —> 十進位制數0.5。
  2. 十進位制數0.625 轉換成二進位制0.101。
  3. 浮點數是指把小數用“符號尾數x 基數的指數次冪”這種形式來表示。
  4. 二進位制數的基數是2,十進位制數的基數是10。以此類推,XX進位制數的基數就是X X。
  5. EXCESS是“剩餘的”的意思。例如,把0111111看作是0 的話,比這個數小1的01111110就是 -1。
  6. 整數部分和小數部分一樣, 二進位制數的4 位,就相當於十六進位制數的1位。

3.1 計算機的誤差

225323-78d9b0fc1a4e56d6.jpg
image

結果:

sum = 10.000002

3.2 用二進位制表示小數

225323-05dd17807d48455b.jpg
image

計算機之所以會出現運算錯誤,是因為“有一些十進位制數的小數無法轉換成二進位制數”。例如,十進位制數0.1,就無法用二進位制數正確表示,小數點後面即使有幾百位也無法表示。

225323-7af8a14278c20f11.jpg
image

因為無法正確表示的數值,最後都變成了近似值。計算機這個功能有限的機器裝置,是無法處理無限迴圈的小數的。因此,在遇到迴圈小數時,計算機就會根據變數資料型別所對應的長度將數值從中間截斷或者四捨五入。

3.4 浮點數

225323-b5e17ea888775c66.jpg
image
  • 單精度浮點數,32位, float
  • 雙精度浮點數, 64位,double
225323-7ac6aee6de4ec06c.jpg
image

3.7 如何避免計算機計算出錯

計算機計算出錯的原因之一是,採用浮點數來處理小數(另外,也
有因“位溢位”而造成計算錯誤的情況)。

  • 將小數轉換成整數計算
  • 在一定誤差範圍內忽略
225323-babb313288d84af0.jpg
image

第4章 熟練使用有稜有角的記憶體

問:

  1. 二進位制數0.1,用十進位制數表示的話是多少?
  2. 用小數點後有3 位的二進位制數,能表示十進位制數0.625 嗎?
  3. 將小數分為符號、尾數、基數、指數4 部分進行表現的形式
    稱為什麼?
  4. 二進位制數的基數是多少?
  5. 通過把 0 作為數值範圍的中間值,從而在不使用符號位的情
    況下來表示負數的表示方法稱為什麼?
  6. 10101100.01010011這個二進位制數,用十六進位制數表示的
    話是多少?

答:

  1. 二進位制數的小數點後第一位的位權是2^-1= 0.5。也就是說,二進位制數0.1—> 1 x 0.5 —> 十進位制數0.5。
  2. 十進位制數0.625 轉換成二進位制0.101。
  3. 浮點數是指把小數用“符號尾數x 基數的指數次冪”這種形式來表示。
  4. 二進位制數的基數是2,十進位制數的基數是10。以此類推,XX進位制數的基數就是X X。
  5. EXCESS是“剩餘的”的意思。例如,把0111111看作是0 的話,比這個數小1的01111110就是 -1。
  6. 整數部分和小數部分一樣, 二進位制數的4 位,就相當於十六進位制數的1位。

4.1 記憶體的物流機制

記憶體IC
- DRAM Dynamic 隨機儲存器, 需要不斷是重新整理電路
- SRAM Staic 隨機儲存器,不需要不斷是重新整理電路
- ROM 只讀儲存器
- 電源
- 地址訊號
- 資料訊號
- 控制訊號

4.2 記憶體的邏輯模型是樓房

225323-b50af514ef66b41e.jpg
image

記憶體為1KB時,表示的是如圖 所示的有1024層的樓房(這裡地址的值是從上往下逐漸變大,不過也有與此相反的
情況)。

225323-b0e53cbfc1c48325.jpg
image

物理上以1個位元組為單位來逐一讀寫資料的記憶體,在程式中,通過指定其型別(變數的資料型別等),也能實現以特定位元組數為單位進行讀寫。

根據程式中所指定的變數的資料型別的不同,讀寫的實體記憶體大小也會隨之發生變化。C 語言中,8位元組(=64 位)的double 型別是最大的。

4.3 簡單的指標

  • 指標也是一種變數
  • 它所表示的不資料的值,而是儲存著資料的記憶體的地址。
  • 通過使用指標,就可以對任意指定地址的資料進行讀寫。
  • 定義指標時候,常在變數名前加一個星號(*)
  • 定義指標的資料型別表示從指標儲存的地址中一次能讀寫的資料位元組數
225323-35f4bfd6631c3a8d.jpg
image
225323-ca045ba269957f88.jpg
image

4.4 陣列

  • 陣列是指多個同樣資料型別的資料在記憶體中連續排列的形式。
  • 作為陣列元素的各個資料會通過連續的編號被區分開來,這個編號稱為索引index
  • 指定索引後,可以對該索引所對應地址的記憶體進行讀寫操作。
  • 索引和記憶體地址的變換工作則是由編譯器自動實現的。
225323-34621d746fcb17b5.jpg
image
  • 陣列可以使程式設計工作變得更加高效,如果在迴圈中反覆使用陣列,使用索引可以很方便的達到按順序進行讀寫陣列元素的目的。

4.5 棧和佇列

在對記憶體資料進行讀寫時

  • 棧用的是LIFO (Last Input First Out,後人先出)方式
  • 佇列用的則是FIFO( First Input First Out,先人先出) 方式
225323-8a9106638ea2419b.jpg
image
225323-00b2bc63abe8d349.jpg
image

佇列的環狀緩衝區(ring buffer )方式

225323-4a6988df3fd5a76b.jpg
image

有6 個元素的陣列來實現一個佇列。從陣列的起始位置開始有序儲存資料,再按照儲存的順序讀出資料。陣列末尾寫人資料後,後一個資料就會被寫人陣列的起始位置(此時資料已經被讀出所以該位置是空的)。這樣,陣列的末尾就和開頭連線了起來,資料的寫人和讀出也就迴圈起來了

4.6 連結串列

連結串列: 使陣列元素的新增和刪除更容易
在陣列的各個元素中,除了資料的值之外,通過為其附帶上下一
個元素的索引,即可實現連結串列。資料的值和下一個元素的索引組合在一起,就構成了陣列的一個元素。

225323-1620971cd23ca680.jpg
image
連結串列刪除元素
225323-d782c8886d71bfb1.jpg
image
連結串列追加元素
225323-d59726435f2a1e9f.jpg
image
  • 如果單純通過移動元素來挪出空間,每次都需要移動數千至數萬個元素,那麼哪怕是高速計算機也會花費很長時間。

4.7 二叉樹

二叉查詢樹: 可以更加高效地對陣列資料進行檢索。

  • 在連結串列的基礎上
  • 往陣列中追加元素時,根據資料的大小
  • 分成左右兩個方向的表現形式
  • 二叉查詢樹是由連結串列構造發展而來的表現形式,因此在追加或刪除元素方面也同樣是有效的。
225323-e0e386cc84769d53.jpg
image
  • 在使用一般的陣列時,必須從陣列的開頭按照索引順序來查詢目標資料。
  • 而使用二叉查詢樹時,當目標資料比現在讀出來的資料小時就可以轉到左側,反之目標資料較大時即可轉到連結串列的右側
  • 這樣就加快了找到目標資料的速度。
225323-fc5e0a9e8e02f84a.jpg
image

第5章 記憶體和磁碟

問:

  1. 儲存程式方式指的是什麼?
  2. 通過使用記憶體來提高磁碟訪問速度的機制稱為什麼?
  3. 把磁碟的一部分作為假想記憶體來使用的機制稱為什麼?
  4. Windows 中,在程式執行時,儲存著可以動態載入呼叫的
    函式和資料的檔案稱為什麼?
  5. 在EXE 程式檔案中,靜態載入函式的方式稱為什麼?
  6. 在Windows 計算機中,一般磁碟的1個扇區是多少位元組?

答:

  1. 在儲存裝置中儲存程式,並逐一執行的方式
  2. Disk Cache( 磁碟快取)
  3. 虛擬記憶體( virtual memory )
  4. DLL( DLL 檔案)
  5. 靜態連結
  6. 512 位元組

記憶體

  • 用電流來實現儲存的記憶體
  • 高速高價

磁碟

  • 利同利用磁效應來實現儲存
  • 低速廉價
  • 磁碟中,利用磁極的不同來標記0、1
225323-76c2c41e6b50083d.jpg
image

5.1 程式的執行需要被讀入記憶體

儲存程式方式(程式內建方式): 程式儲存在儲存裝置中,通過有序地被讀出來實現執行

原因: 負責解析和執行程式內容的CPU,
需要通過內部程式計數器來指定記憶體地址,然後才能讀出程式。

CPU可以直接從讀磁碟中儲存的程式,但是磁碟讀取速度太慢,程式的執行速度會大大降低。

225323-2176c259925e8373.jpg
image

5.2 磁碟快取加快了磁碟訪問速度

磁碟快取

  • 指的是把從磁碟中讀出的資料儲存到記憶體空間中的方式。

  • 需要讀取同一資料時,就不用通過實際的磁碟,而是從磁碟快取中把內容讀出。加快訪問速度

225323-47d9ea042c8652e7.jpg
image

5.3 虛擬記憶體把磁碟作為部分記憶體來使用

虛擬記憶體( virtual memory )

  • 虛擬儲存:在有限容量的記憶體中,以頁為單位自動裝入更多更大的程式
  • 虛擬記憶體是指把磁碟的一部分作為假想的記憶體來使用
  • 這與磁碟快取是假想的磁碟(實際上是記憶體)相對,虛擬記憶體是假想的記憶體(實際上是磁碟)

實現方式

  • 覆蓋(overlay):應用程式手動把需要的指令和資料儲存在記憶體中
  • 交換(swapping):作業系統自動把暫時不能執行的程式儲存到外存中

CPU 只能執行載入到記憶體中的程式。虛擬記憶體雖說是把磁碟作為記憶體的一部分來使用,但實際上正在執行的程式部分,在這個時間點上是必須存在在記憶體中的

為了實現虛擬記憶體,就必須把實際記憶體(也可稱為實體記憶體)的內容,和磁碟上的虛擬記憶體的內容進行部分置換(swap ),並同時執行程式。

虛擬記憶體是計算機系統記憶體管理的一種技術。它使得應用程式認為它擁有連續可用的記憶體(一個連續完整的地址空間),而實際上,它通常是被分隔成多個實體記憶體碎片,還有部分暫時儲存在外部磁碟儲存器上,在需要時進行資料交換

225323-651ae681b3c5389c.jpg
image

虛擬記憶體無法徹底解決記憶體不足的問題

  • 而虛擬記憶體也確實能避免因記憶體不足導致的應用無法啟動。
  • 不過,由於使用虛擬記憶體時發生的Page In 和Page Out 往往伴隨著低速的磁碟訪問,因此在這個過程中應用的執行會變得遲鈍起來。
  • 虛擬記憶體無法徹底解決記憶體不足的問題。
225323-a40678020186ead6.jpg
image

5.4 節約記憶體的程式設計方法

從根本上解決記憶體不足的問題

  • 需要增加記憶體的容量
  • 儘量把執行的應用檔案變小

(1)通過DLL 檔案實現函式共有
DLL( Dynamic Link Library )檔案”,顧名思義,是在程式執行時可以動態載入Library( 函式和資料的集合)的檔案。

那就是多個應用可以共有同一個DLL 檔案。而通過共有同一個DLL 檔案則可以達到節約記憶體的效果。

225323-1515337a32d4bae6.jpg
image
225323-f477c661bb0316e3.jpg
image

棧清理

225323-764a9b282616f14b.jpg
image

225323-d6314a0de47dd10a.jpg
image

5.5 磁碟的物理結構

225323-ae14a3e659b229ef.jpg
image

扇區是對磁碟進行物理讀寫的最小單位。Windows 中使用的磁碟, 一般1個扇區是512 位元組。不過,Windows 在邏輯方面(軟體方面)對磁碟進行讀寫的單位是扇區整數倍簇。根據磁碟容量的不同,1簇可以
是512位元組(1簇=1扇區)、1KB(1簇=2扇區)、2KB、4KB、8KB、16KB、32KB (1簇= 64 扇區)。磁碟的容量越大,簇的容量也越大。不過,在軟盤中,1簇=512 位元組= 1扇區,簇和扇區的大小是相等的。

附:

硬碟的儲存原理是什麼?為什麼一張小小的硬碟可以存下如此多的資料?

磁鐵有兩個極性, 一個是南極(S極) , 一個是北極(N 極) , 硬碟正是利用磁粒子的極性來記錄資料的。
碟片表面的那些磁粉就是磁粒子。碟片被劃分成若干個同心圓( 稱為磁軌),在每個同心圓的磁軌上就好像有無數的任意排列的小磁鐵, 當這些小磁鐵受到來自磁頭磁場的影響時,排列的方向隨之改變, 利用磁頭的磁力統一某區域小磁鐵的方向, 就可以使該區域磁場呈現相同極性, 如果把 S/N 兩種極性與二進位制中的 0和 1 對應, 就可以表示二進位制資料, 這些磁粒子都是永磁體, 即便磁頭離開, 它依然可以長時間保持形成的極性, 這樣就能達到儲存資訊的目的了。
磁頭在讀取資料時, 可以感應磁粒子的不同極性, 從而轉換成不同的電脈衝訊號, 利用解碼器將這些原始訊號翻譯出來, 就成為了電腦能使用的資料。

固態硬碟原理

  • 固態硬碟(Solid State Drives),用固態電子儲存晶片陣列而製成的硬碟,由控制單元和儲存單元(FLASH晶片、DRAM晶片)組成。
  • 在儲存單元電晶體的柵(Gate)中,注入不同數量的電子,通過改變柵的導電效能,改變電晶體的導通效果,實現對不同狀態的記錄和識別。

磁碟碎片

如果檔案都一個挨著一個,緊緊的貼放在一起的話,那麼讀取他們將會非常的容易和迅速,這是因為在硬碟裡動得最慢的(相對來說)就是傳動手臂,少位移一些,讀取檔案資料的時間就會快一些。

但如果其中某個檔案要更改的話,那麼就意味著接下來的資料將會被放在磁碟其他的空餘的地方。

如果這個檔案被刪除了,那麼就會在系統中留下空格,久而久之,我們的檔案系統就會變得支離破碎,碎片就是這麼產生的。

其實我們的檔案大多數的時候都是破碎的,在檔案沒有破碎的時候,搖臂只需要尋找1次磁軌並由磁頭進行讀取,只需要1次就可以成功讀取;但是如果檔案破碎成 11處,那麼搖臂要來回尋找11次磁軌磁頭進行11次讀取才能完整的讀取這個檔案,讀取時間相對沒有破碎的時候就變得冗長.

225323-2f742ec906bab134.jpg
image
固態硬碟(SSD)不需要整理磁碟碎片

固態硬碟(SSD)每個塊的讀取時間都是恆定的,不像機械硬碟讀取物理連續的扇區才有最快速度(因此機械硬碟才要“整理碎片”)。而且SSD的寫入壽命(按次數)比機械硬碟少一到兩個數量級,經常整理硬碟會大大加速老化。

參考

  1. 虛擬記憶體,頁面置換演算法 - 簡書

第7章 程式是在何種環境中執行的

7.1 執行環境 = 作業系統 + 硬體

  • 原生程式碼: 機器語言的程式稱為原生程式碼(native code )

  • 原始碼: 程式設計師用C 語言等編寫的程式: 在編寫階段僅僅是文字檔案。文字檔案(排除文字編碼的問題) 在任何環境下都能顯示和編輯。

  • 原生程式碼: 通過對原始碼進行編譯,就可以得到原生程式碼。

225323-b0b8d26d39e418ca.jpg
image

API : 應用程式向作業系統傳遞指令的途徑稱為API (Applicati on Programming Interface)"

虛擬機器

在程式執行時,將編譯後的位元組程式碼轉換成原生程式碼,這樣的操作方法看上去有些遷回,但由此可以實現同樣的位元組程式碼在不同的環境下執行。

225323-d0a0b62230ec16db.jpg
image

7.7 BIOS 和引導

BIOS

  • BIOS( Basic Input/Output System)

  • 開機後,BIOS 會確認硬體是否正常執行,沒有問題的話就會啟動載入程式。載入程式的功能是把在硬碟等記錄的OS 載入到記憶體中執行。

  • BIOS 除了鍵盤、磁碟、顯示卡等基本控制程式外,還有啟動“載入程式”的功能。載入程式是儲存在啟動驅動器起始區域的小程式。

  • 作業系統的啟動驅動器一般是硬碟,不過有時也可以是CD-ROM 或軟盤。

  • 雖然啟動應用是OS 的功能,但OS 並不能自己啟動自己,而是通過載入程式來啟動。

225323-6fce46b57ac67746.jpg
image

第8章 從原始檔到可執行檔案

8.1 計算機只能執行原生程式碼

225323-661e51d73d86612c.jpg
image

8.3 編譯器負責轉換原始碼

  • 能夠把C 語言等高階程式語言編寫的原始碼轉換成原生程式碼的程式稱為編譯器。
  • 每個編寫原始碼的程式語言都需要其專用的編譯器。
  • 將C 語言編寫的原始碼轉換成原生程式碼的編譯器稱為C 編譯器。
225323-cb4ceda1a941627c.jpg
image

8.6 DLL 檔案及匯入庫

225323-b02ed639eae0a3d6.jpg
image

8.7 可執行檔案執行時的必要條件

EXE 檔案的執行機制

  • EXE 檔案是作為單獨的檔案儲存在硬碟中的
  • 通過資源管理器找到並雙擊EXE 檔案,就會把EXE 檔案的內容載入到記憶體中執行。
225323-559d32868e243d4c.jpg
image

程式載入到記憶體後,還會額外生成兩個組,那就是棧和堆。

棧是用來儲存函式內部臨時使用的變數(區域性變數), 以及函式呼叫時所用的引數的記憶體區域。
堆是用來儲存程字執行時的任意資料及物件的記憶體領域

225323-1a5761d2eb42e92b.jpg
image

記憶體洩露(memory leak) : 如果沒有在程式中明確釋放堆的記憶體空間,那麼即使在處理完畢後,
該記憶體空間仍會一直殘留。

  • 棧及堆的記憶體空間都是在程式執行時得到
  • 棧中申請分配的對資料進行儲存和捨棄(清理處理)的程式碼,是由編譯器自動生成的,
  • 使用棧的資料的記憶體空間,每當函式被呼叫時都會得到申請分配,並在函式處理完畢後自動釋放。
  • 堆的記憶體空間,則要根據程式設計師編寫的程式,來明確進行申請分配或釋放。

第9章 作業系統和應用的關係

  • 為了提高特定處理效率的程式總稱為“應用”。
  • 利用計算機執行程式大部分都是為了提高處理效
225323-e4eacaf4df99e527.jpg
image

作業系統本身並不是單獨的程式,而是多個程式
的集合體

225323-6c5f7ae83fcb802d.jpg
image
225323-9888a19e5f1004e1.jpg
image

9.2 要意識到作業系統的存在

225323-f0364cda2aa3421b.jpg
image

9.3 系統呼叫和高階程式語言的移植性

  • 高階程式語言並不依存於特定的作業系統
225323-44e789a535b60309.jpg
image

9.4 作業系統和高階程式語言使硬體抽象化

  • 通過使用作業系統提供的系統呼叫,程式設計師就沒必要編寫直接控制硬體的程式了。
  • 通過使用高階程式語言,有時甚至也無需考慮系統呼叫的存在。
  • 這是因為作業系統和高階程式語言能夠使硬體抽象化。
225323-cada58814745500f.jpg
image

提供多工功能

225323-a4d3dd34898d01b7.jpg
image

第10章 通過彙編語育瞭解程式的實際構成

225323-ae720d4152426764.jpg
image

10.4組合語言的語法是“操作碼+ 運算元’

225323-763a9d5f73b23abc.jpg
image

10.5 最常用的mov 指令

225323-d9f520a9c2d2e68a.jpg
image

10.6 對棧進行push 和POP

225323-6eccd416baabe8a9.jpg
image
  • 棧是儲存臨時資料的區域,
  • 它的特點是通過push 指令和POP 指令
  • 進行資料的儲存和讀出。往棧中儲存資料稱為“人棧”
  • 從棧中讀出資料稱為“出棧”。
  • 32 位X86 系列的CPU 中,進行1次push 或pop,即可處理32 位(4 位元組)的資料。
225323-80648f59cf99292f.jpg
image

10.8 函式內部的處理

225323-2173ca35e2df4757.jpg
image

10.9 始終確保全域性變數用的記憶體空間

  • 全域性變數:在函式外部定義的變數
  • 區域性變數: 在函式內部定義的變數
225323-e7b9090c22115f3d.jpg
image

225323-12206340c5264a85.jpg
image

10.10 臨時確保區域性變數用的記憶體空間

  • 區域性變數是臨時儲存在暫存器和棧中的
  • 函式內部利用的棧,在函式處理完畢後會恢復到初始狀態
  • 區域性變數的值也就被銷燬
225323-efbffd4becfb09c4.jpg
image
225323-a939c75067b7a1b8.jpg
image

第11章 硬體控制方法

11.1 應用和硬體無關?

225323-4a1c1d944973d4e6.jpg
image

11.2 支撐硬體輸入輸出的IN 指令和OUT 指令

225323-ab0b4506683620ca.jpg
image

第12章 讓計算機“思考”

12.1 作為“工具”的程式和為了“思考”的程式

  • 程式就如同是由計算機執行的各種指令羅列起來的文章。

  • 計算機內部的CPU, 通過對該文章的內容進行解析和執行,來控制連線到計算機的各種外圍裝置。

  • 具體來說,控制就是指CPU 和各種裝置之間配合進行資料的輸人輸出處理。

225323-b6ff10d6daa8abaf.jpg
image

12.2 用程式來表示人類的思考方式

225323-8603efa63f227f7f.jpg
image

變數和函式

225323-0bc8c661ac5ebf27.jpg
image
225323-2195880f9513cee9.jpg
image

資料型別

225323-4dfe859a65db5736.jpg
image

結語

  • 記得有“自己嚇唬自己是最可怕的事情”這樣的說法。

  • 如果總是想一些令自己擔心恐懼的事情,枯萎的花朵都能被看成幽靈

  • 在瞭解程式的實質前,大家也許會覺得程式很難。面對困難,我們會感到恐懼

  • 不過,對已經讀過本書的各位讀者來說,程式設計應該不再是那麼可怕的事情了吧。

  • 程式的執行機制其實很簡單,這一點想必大家也都有

  • 了切身體會。

  • 不管今後的計算機怎麼發展,程式的實質是不會發生太大變化的。

  • 因此,請大家務必放鬆心情,無所畏懼地繼續向新技術發起挑戰吧!

相關文章