計(jì)算機(jī)漫游1-Computer Systems筆記之編譯系統(tǒng)
從helloworld程序的前半生簡(jiǎn)單介紹編譯系統(tǒng)
一個(gè)簡(jiǎn)單的hello.c程序:
#include
Int main(){
? ? Printf(“hello, world!\n”);
}
在linux下在shell中直接輸入:gcc -0 hello hello.c然后找到home目錄愿吹,就可以發(fā)現(xiàn)多出來(lái)了一個(gè)hello可執(zhí)行程序绣檬。然后再在shell中敲入:./hello就可以看到shell上已經(jīng)打印出“hello搀玖,world!”字符串了妄荔。
先憑自己理解的畫個(gè)hello.c文件運(yùn)行的過(guò)程:
上面的圖用最簡(jiǎn)潔語(yǔ)言描述就是:創(chuàng)建-->運(yùn)行-->出結(jié)果-->終止箩绍。但是礁扮,看似簡(jiǎn)單的東西背后一般都有很復(fù)雜的過(guò)程就珠。為了搞清楚這個(gè)過(guò)程,可以詢問(wèn)下hello.c它在這個(gè)過(guò)程中都經(jīng)歷了啥瓷耙。
問(wèn):hello.c朱躺,你從出生到進(jìn)入休息狀態(tài)都經(jīng)歷了什么啊搁痛?
Hello.c:我經(jīng)歷的事情可多了长搀!嗯,主要?dú)v程分為兩個(gè)(因?yàn)橹魅司驮趕hell中輸入了兩條命令)鸡典,先說(shuō)說(shuō)我的前半生—在編譯系統(tǒng)中經(jīng)歷的四次格式轉(zhuǎn)換過(guò)程吧:
首先源请,主人通過(guò)“一號(hào)輸入小幫手”鍵盤把我送進(jìn)了電腦的硬盤里頭存起來(lái),
接著主人在”口令發(fā)號(hào)中心“shell中用命令“gcc -o hello hello.c”告訴系統(tǒng)要執(zhí)行我,系統(tǒng)收到這個(gè)命令巢钓,就開始了我前半生的風(fēng)雨漂流:
我先是來(lái)到了四哥“預(yù)處理器”的辦公場(chǎng)所病苗,看見有兩個(gè)倉(cāng)庫(kù):一個(gè)寫著“#include”,一個(gè)寫著“#define”症汹。我查了查自己的程序文本硫朦,發(fā)現(xiàn)我恰好有“#include”開頭的東西,走進(jìn)去后四哥立即將stdio.h放到我的程序文本中背镇,這時(shí)候咬展,我從hello.c變成了hello.i,表示“被修改的源程序”瞒斩;
緊接著破婆,我被送往三哥“編譯器”那里,三哥太厲害了胸囱,三下五除二就把我翻譯成了二哥“匯編器”能識(shí)別的匯編語(yǔ)言祷舀,這樣,我的每條語(yǔ)句都以一種標(biāo)準(zhǔn)的文本格式確切地描述了一條低級(jí)機(jī)器語(yǔ)言指令烹笔,此時(shí)裳扯,我又從hello.i變成了hello.s,表示“匯編程序”谤职;
然后饰豺,我就來(lái)到了二哥“匯編器”這里,二哥告訴我大哥“連接器”比他更厲害允蜈,但是很懶冤吨,在掌握了超級(jí)難的機(jī)器語(yǔ)言后就不想再去學(xué)其他東西了,所以才有了他們?nèi)齻€(gè)弟弟饶套,(其實(shí)應(yīng)該是在說(shuō)主人很笨吧漩蟆,看不懂機(jī)器語(yǔ)言和匯編吧:(),于是凤跑,我被二哥翻譯成了機(jī)器語(yǔ)言指令爆安,并把這些指令打包成一種叫做“可重定位目標(biāo)程序”的格式叛复,然后呢仔引,我終于從hello.s變成了目標(biāo)文件hello.o了(此時(shí)我也搖身一變,從文本文件變成了二進(jìn)制文件)褐奥!要是主人在這個(gè)時(shí)候打開我咖耘,看到的就將是一堆亂碼。
最后撬码,我終于來(lái)到了大哥“鏈路器”這里儿倒,并在大哥的幫助下,找到了程序中printf函數(shù)需要的另一個(gè)標(biāo)文件printf.o,大哥將我們合并夫否,由此彻犁,我們就成了“可執(zhí)行”的目標(biāo)文件hello了。
然后我的前半生就到這了凰慈,回到磁盤中休息并等待主人的第二次命令汞幢,需要我的時(shí)候再出來(lái)。
老師告訴我上面hello.c經(jīng)歷上午四個(gè)過(guò)程剛好構(gòu)成編譯系統(tǒng)(compilation system):
1)預(yù)處理階段(Preprocessing phase):預(yù)處理器(preprocessor微谓,cpp)根據(jù)字符“#”開頭的命令修改原始的C程序(就是由你自己寫的那個(gè)程序)森篷,即插入所有用#include命令指定的文件,擴(kuò)展所有用#define聲明指定的宏豺型。例如仲智,hello.c文件中第一行的“#include ”命令告訴預(yù)處理器去讀取系統(tǒng)頭文件stdio.h的內(nèi)容,并把它插入到程序文本中姻氨。然后hello.c的后綴就是.i钓辆。(這一步可以用命令”gcc -E hello.c -o hello.i”查看預(yù)處理過(guò)程)
2)編譯階段(Compilation phase):編譯器(compiler,ccl)將文本文件hello.i轉(zhuǎn)化為由匯編語(yǔ)言程序構(gòu)成的hello.s文本文件肴焊。每一條匯編指令都包括確切的對(duì)于一個(gè)低級(jí)機(jī)器語(yǔ)言指令的描述岩馍。(這一步通過(guò)命令”gcc -S hello.i -o hello.s”查看)
3)匯編階段(Assembly phase):匯編器(assembler,as)將文本文件hello.s轉(zhuǎn)化成由機(jī)器語(yǔ)言構(gòu)成的hello.o二進(jìn)制文件抖韩。hello.o是機(jī)器代碼的一種形式蛀恩,包含所有指令的二進(jìn)制表示,但還未填入地址的全局值茂浮,比如全局變量的地址(這一步可用命令”gcc -c hello.s -o hello.o)
4)鏈接階段(Linking phase):因?yàn)閔ello程序調(diào)用了printf函數(shù)双谆,而printf函數(shù)存在于一個(gè)名為printf.o的單獨(dú)編譯好的目標(biāo)文件中,鏈路器(linker席揽,ld)就負(fù)責(zé)將printf.o與hello.o合并起來(lái)的工作顽馋,最后得到hello可執(zhí)行目標(biāo)文件,可以被加載到內(nèi)存中幌羞,由系統(tǒng)執(zhí)行寸谜。(其實(shí)hello就是hello.exe,在執(zhí)行時(shí)可以將后綴省略属桦,使用命令”gcc hello.o –o hello.exe”)
轉(zhuǎn)換文件如下:
hello.c
hello.i
hello.s
hello.o
最后就是可執(zhí)行文件hello了熊痴,這里就不展示了,因?yàn)橥琱ello.o一樣也是一堆亂碼聂宾。