計算機(jī)是由硬件和系統(tǒng)軟件組成导梆,它們共同工作來運(yùn)行應(yīng)用程序晚岭。
我們來通過hello程序生命周期,了解當(dāng)系統(tǒng)在執(zhí)行hello程序時恭陡,系統(tǒng)發(fā)生了什么以及為什么會如此運(yùn)作蹬音。
#include <stdio.h>
int main()
{
printf("hello world\n");
}
一、信息是位+上下文
hello程序時從源程序(源文件)開始休玩,源程序由程序員通過編輯器創(chuàng)建并保存為文本文件著淆,文件名為hello.c。源程序?qū)嶋H上是由0和1組成的位(亦比特)序列拴疤,這些位被組織成8個一組永部,稱為字節(jié),每個字節(jié)表示程序中某個文本字符呐矾。
大部分程序采用ASCII標(biāo)準(zhǔn)來表示文本字符苔埋,用唯一的字節(jié)大小的整數(shù)值表示每個字節(jié)。
hello.c以字節(jié)序列方式存儲在文件中蜒犯,每個字節(jié)都有一個整數(shù)值组橄,對應(yīng)于某個字符荞膘。例如,第一個字節(jié)的整數(shù)值是35玉工,對應(yīng)字符#羽资。每行文本都是以一個看不見換行符"\n"來結(jié)束,對應(yīng)的整數(shù)值為10遵班。
hello.c的標(biāo)識方法說明一個基本思想:系統(tǒng)中所有的信息-----包含磁盤文件削罩、存儲器中的程序、存儲器中存放的用戶數(shù)據(jù)以及網(wǎng)絡(luò)上傳送的數(shù)據(jù)费奸,都是由一串比特表示的弥激。區(qū)分不同數(shù)據(jù)對象的唯一方法,讀到這些數(shù)據(jù)對象時上下文愿阐。
二微服、程序被其他程序翻譯成不同格式
為了在系統(tǒng)上運(yùn)行hello.c程序,每條C語句都必須被其他程序轉(zhuǎn)化為一系列的低級機(jī)器語言指令缨历,這些指令按照可執(zhí)行目標(biāo)程序的格式打包以蕴,并以二進(jìn)制磁盤文件的形式存放起來。
在Unix系統(tǒng)上辛孵,從源文件到目標(biāo)文件的轉(zhuǎn)化是由編譯器驅(qū)動程序完成丛肮,
unix> gcc -0 hello hello.c
gcc編譯器驅(qū)動程序讀取源程序文件hello.c,翻譯成一個可執(zhí)行目標(biāo)文件hello魄缚,這個翻譯過程分為四個階段完成宝与,預(yù)處理器、編譯器冶匹、匯編器习劫、鏈接器。
- 預(yù)處理階段嚼隘。預(yù)處理(cpp)根據(jù)字符#開頭的命令诽里,修改原始C程序,直接插入到程序文本中飞蛹,得到來一個C程序谤狡,以.i作為文件擴(kuò)展名
- 編輯階段。編譯器(ccl)干湖文本文件hello.i翻譯成文本文件hello.s卧檐,包含匯編語言程序墓懂。匯編語言程序中每條語句以一種標(biāo)準(zhǔn)文本格式確切地描述一條低級機(jī)器語言指令。匯編語言為不同高級語言的不同編譯器提供了通用輸出語言
- 匯編階段泄隔。匯編器(as)將hello.s翻譯成機(jī)器語言指令拒贱,把這些指令打包成可重定位目標(biāo)程序格式,將結(jié)果保存在目標(biāo)文件hello.o中佛嬉,hello.o是二進(jìn)制文件逻澳,字節(jié)編碼是機(jī)器語言指令而不是字符,在文本編輯器打開hello.o文件暖呕,呈現(xiàn)亂碼
- 鏈接階段斜做。鏈接器(ld)并入標(biāo)準(zhǔn)庫函數(shù)printf,得到hello可執(zhí)行文件湾揽∪勘疲可執(zhí)行文件加載到存儲器后,由系統(tǒng)負(fù)責(zé)執(zhí)行库物。
三霸旗、了解編譯系統(tǒng)如何工作的好處
- 優(yōu)化程序性能。比如一個switch語句是不是比一系列if-then-else語句高效戚揭?一個函數(shù)調(diào)用代價多少诱告?while循環(huán)比do循環(huán)更有效嗎?為什么兩個功能相近的循環(huán)的運(yùn)行時間會有很大的差異民晒?
- 理解鏈接時出現(xiàn)錯誤精居。鏈接器報無法解析一個引用,是什么意思潜必?靜態(tài)庫和動態(tài)庫的區(qū)別是什么靴姿?為什么有些鏈接錯誤直到運(yùn)行時才出現(xiàn)?
- 避免安全漏洞磁滚。緩沖區(qū)溢出錯誤造成大多數(shù)網(wǎng)絡(luò)和Internet服務(wù)器上的安全漏洞佛吓。
四、處理器讀并解釋在存儲器中的指令
(1) 系統(tǒng)硬件組成
為了理解運(yùn)行hello程序發(fā)生了什么垂攘,需要理解典型系統(tǒng)的硬件組織辈毯。
CPI:中央處理器 ALU:算數(shù)/邏輯運(yùn)算單元 PC:程序計數(shù)器 USB:通用串行總線
(a)總線
貫穿整個系統(tǒng)是一組電子管道,稱為總線搜贤,攜帶信息字節(jié)并在各個部件間傳遞谆沃。總線傳送定長字節(jié)塊
(b)I/O設(shè)備
I/O(輸入仪芒、輸出)設(shè)備是系統(tǒng)與外界的聯(lián)系通道唁影。上述有2個I/O設(shè)備,用戶輸入的鍵盤和鼠標(biāo)掂名,作為用戶輸出的顯示器据沈,用于長期存儲數(shù)據(jù)和程序的磁盤驅(qū)動器。最開始,可執(zhí)行程序hello放在磁盤上饺蔑。
每個I/O設(shè)備通過控制器或適配器與I/O總線連接起來锌介,控制器和適配器區(qū)別在于組成方式。控制器是I/O設(shè)備中或系統(tǒng)主印制電路板(通常為主板)上的芯片組孔祸,適配器一塊插在主板槽上的卡隆敢。功能是I/O總線和I/O設(shè)備之間傳遞信息。
(c)主存
臨時存儲設(shè)備崔慧,處理器執(zhí)行程序時拂蝎,存放程序和程序處理的數(shù)據(jù)。主存是DRAM動態(tài)隨機(jī)存取存儲器芯片組成惶室。邏輯上温自,存儲器由一個線性字節(jié)數(shù)組組成,每個字節(jié)有唯一的地址(數(shù)組索引)皇钞,從零開始悼泌。一般來說,組成程序的每條機(jī)器指令由不定量字節(jié)構(gòu)成夹界。
(d)處理器
中央處理單元(CPU)簡稱處理器馆里,執(zhí)行存儲在主存中指令的引擎。處理器核心是一個成為程序計數(shù)器(PC)的字長大小的存儲設(shè)備(或寄存器)掉盅。在任何一個時間節(jié)點(diǎn)也拜,PC指向主存中的某條機(jī)器語言指令(內(nèi)含地址)
從系統(tǒng)通電開始,直到系統(tǒng)斷電趾痘,處理器一直在重復(fù)執(zhí)行相同的基本任務(wù):從程序計數(shù)器(PC)指向的儲存器處讀取指令慢哈,解釋指令中的位,執(zhí)行只來指示的簡單操作永票,然后更新程序計數(shù)器指向下一條指令卵贱,這條指令并不一定在存儲器中和剛剛執(zhí)行的指令相鄰。
CPU在指令的要求下可能執(zhí)行的操作
- 加載:從主存拷貝一個字節(jié)或者一個字到寄存器侣集,覆蓋寄存器原來的內(nèi)容
- 存儲:從寄存器拷貝一個字節(jié)或者一個字到主存著某個位置键俱,覆蓋這個位置上原來的內(nèi)容
- 更新:拷貝兩個寄存器的內(nèi)容到ALU,ALU將兩個字相加世分,并將課堂存放到一個寄存器中编振,覆蓋該寄存器中原來的位置
- I/O讀:從一個I/O設(shè)備中拷貝一個字節(jié)或者一個字到寄存器
- I/O寫:從一個寄存器拷貝一個字節(jié)或一個字到I/O設(shè)備
- 轉(zhuǎn)移:從指令本身中抽取一個字,并將這個字拷貝到程序計數(shù)器(PC)中臭埋,覆蓋PC中原來的值
(2)執(zhí)行hello程序
shell程序執(zhí)行它的指令踪央,當(dāng)在鍵盤上輸入字符串./helo,shell程序逐一讀取字符到寄存器瓢阴,再把它存放到存儲器中畅蹂。
當(dāng)我們岸上enter鍵,shell知道結(jié)束命令的輸入荣恐,shell執(zhí)行一系列指令液斜,將hello目標(biāo)文件中的代碼和數(shù)據(jù)從磁盤拷貝到主存累贤,從而加載hello文件。
利用DMA(隨機(jī)存儲器存取)技術(shù)少漆,數(shù)據(jù)可以不通過處理器直接從磁盤到主存
一旦hello目標(biāo)文件中的代碼和數(shù)據(jù)加載到存儲器臼膏,處理器開始執(zhí)行hello程序的主程序中的機(jī)器語言指令。這些指令將hello,world\n串的字節(jié)從存儲器拷貝到寄存器文件检疫,再從寄存器中文件拷貝到顯示設(shè)備讶请,最終顯示在屏幕上祷嘶。