《深入理解計算機系統》學習筆記
hello world往往是學習編程時遇到的第一個示例,下面是一個C語言版本:
#include <stdio.h>
int main()
{
printf("hello, world\n");
return 0;
}
這個程序會在終端打印hello, world
這串字符,那麼這個程序在我們的計算機上到底是怎能被讀取運行的呢?
程序的存儲
我們將這段代碼保存爲文本文件並命名爲hello.c,稱作源程序或源文件。這段代碼裏都是英文字母以及一些符號,然而,計算機內存儲的信息都是二進制的,計算機只能識別由1和0組成的位(比特)序列,每8個位組成一個字節。那麼英語有26個字母,漢字更是成千上萬個,只用0和1怎麼表示呢?不用怕,我們只要將很多個0與1排列組合一下就能表示很多個了,n位就能表示2n個文字。例如一個字節8位最多就能表示256個。這就是編碼的強大能力了,常見的編碼標準有ASCII和UTF-8。
程序被翻譯成不同格式
C語言是高級語言,要想在計算機上運行,就必須被翻譯成計算機能理解的機器語言指令。然後這些指令按一種稱爲可執行目標程序的格式打好包,並以二進制磁盤文件的形式存放起來。目標程序也稱可執行目標文件。linux> gcc -o hello hello.c
在Linux上我們用GCC將hello.c翻譯稱可執行文件hello,這個過程可以分爲四個階段。
-
預處理階段:預處理器cpp根據以井號#開頭的預處理命令修改原始程序。例如這裏hello.c的第一行
#include <stdio.h>
告訴預處理器讀取頭文件stdio.h的內容(這個文件裏包含了printf
這個函數)並把它插入源程序,得到hello.i
。 -
編譯階段:編譯器ccl將
hello.i
翻譯成文本文件hello.s
,這裏包含一個彙編語言程序:
main:
subq $8, %rsp
movl $LC0, %edi
call puts
movl $0, %eax
addq $8, %rsp
ret
-
彙編階段:彙編器as將hello.s翻譯成機器語言指令,把這些指令打包成一種叫做可重定位目標程序的格式,並將結果保存到
hello.o
-
鏈接階段:我們在程序裏用到了
printf
函數,它存在於一個名爲printf.o
的預編譯好了的文件中。這個文件需要以某種方式合併到我們的程序中,這就要用到鏈接器ld。結果就得到可執行文件hello
。可以被加載到內存由系統執行。