電腦系統->Hello World的一生 | 程式如何運行

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工具鏈有哪些?