計算機系統->Hello World的一生 | 程式如何執行

climerecho 發表於 2021-11-27

2021年11月27日準備發在基地微信公眾號上的推文。

綜合了多篇大佬的部落格,以及自己已經知道的知識,對一些疑惑進行了現階段我認為還算滿意的解答。

不過又產生了很多疑問:

  1. 記憶體和磁碟的關係
  2. CPU是如何執行機器指令的(雖然大概想過去會是數字邏輯上的電路的組合)
  3. ......

========================================================================================================================================

我寫的上一篇推文是:Vscode裡的多檔案編譯,本來這一篇計劃是多語言混編,可是還沒玩太明白,就來補充講一講程式的執行過程,對上一篇也是一種補充。

當我們要讓一個程式執行起來,如下面這段程式碼:

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

需要以下過程:

1. 預處理

 gcc -E hello.c -o hello.i 

這一過程主要是處理原始碼檔案中:

  • 以”#”開始的預編譯指令

    如”#include”、”#define”等

    將include的檔案插入進來,將所有define的巨集定義展開。

  • 刪除註釋

  • 新增行號和檔名標識。

    以便編譯時編譯器產生除錯用的行號資訊及用於編譯時產生編譯錯誤或警告時能夠顯示行號

2. 編譯

 gcc -S hello.i -o hello.s 

編譯程式(Compiler)把預處理完的檔案進行一系列詞法分析、語法分析、語義分析及優化後生產相應的彙編程式碼檔案。

當然,預處理和編譯可以合二為一,直接一步到位:

 1 gcc -S hello.c -o hello.s 

這裡提一下這個gcc,這只是後臺編譯程式(compiler)的控制檯,會根據不同引數去呼叫不同的預處理、編譯程式,來處理不同的語言。

3. 彙編

 1 gcc -c hello.c -o hello.o 

.o檔案就是“目標檔案”(Object File),將彙編語句轉換為機器語言,機器語言是CPU可以執行的命令。

4. 連結

在上面的過程中,原本的一個檔案會生成若干個目標模組,這些模組是割裂的。

由連結程式(Linker)將這些目標模組(程式段),以及它們所需要的庫函式連結在一起,形成一個完整的裝入模組(Load Module);這是完整的執行命令的可執行檔案exe(此時已經是二進位制檔案)。

連結分為靜態連結和動態連結。不展開了。

DLL檔案就是Dynamic Link Library檔案.

5. 裝入

可執行檔案只有裝載到記憶體以後才能被CPU執行。

裝入過程就是由裝入程式(Loader)將裝入模組裝入實體記憶體。實體記憶體就是真實存在的記憶體條。

實體記憶體是由若干個儲存單元組成的,每個儲存單元有一個編號,這種編號可唯一標識一個儲存單元,稱為記憶體地址(或實體地址)。

可以簡單理解成類陣列的一個結構。

這個過程進行了地址重定位,主要是將邏輯地址轉換成實體記憶體的絕對地址(相當於拿考號找座位)。這個過程只有在程式將要被執行的時候才會發生。

圖片

6. 執行

執行不是一個嚴格的概念,只是我用來描述裝入到輸出“Hello World”這個過程的一個名詞。

當計算機要執行該程式時,二進位制檔案中相關的指令會傳送給CPU,經過CPU的操作,顯示到了顯示器上。

7. GCC

補充GCC的工具鏈

img

8 複習

  1. 程式從按下“編譯”到裝入記憶體需要哪些過程?
  2. GCC工具鏈有哪些?