匯編語(yǔ)言:一種用于電子計(jì)算機(jī)、微處理器、微控制器,或其他可編程器件的低級(jí)語(yǔ)言。在不同的設(shè)備中,匯編語(yǔ)言對(duì)應(yīng)著不同的機(jī)器語(yǔ)言指令集。一種匯編語(yǔ)言專用于某種計(jì)算機(jī)系統(tǒng)結(jié)構(gòu),而不像許多高級(jí)語(yǔ)言,可以在不同系統(tǒng)平臺(tái)之間移植。使用匯編語(yǔ)言編寫(xiě)的源代碼,然后通過(guò)相應(yīng)的匯編程序?qū)⑺鼈冝D(zhuǎn)換成可執(zhí)行的機(jī)器代碼。這一過(guò)程被稱為匯編過(guò)程。由于匯編更接近機(jī)器語(yǔ)言,能夠直接對(duì)硬件進(jìn)行操作,生成的程序與其他的語(yǔ)言相比具有更高的運(yùn)行速度,占用更小的內(nèi)存,因此在一些對(duì)于時(shí)效性要求很高的程序、許多大型程序的核心模塊以及工業(yè)控制方面大量應(yīng)用。(https://zh.wikipedia.org/wiki/%E6%B1%87%E7%BC%96%E8%AF%AD%E8%A8%80)
hello程序的生命周期是從一個(gè)源程序開(kāi)始的,即程序員利用編輯器創(chuàng)建并保存的文本文件,文件名是hello.c。源程序?qū)嶋H上就是個(gè)由值0和1組成的位(bit)序列。8個(gè)位被組織成一組,稱為字節(jié)。每個(gè)字節(jié)表示程序中的某個(gè)文本字符。大部分的現(xiàn)代系統(tǒng)都使用ASCII標(biāo)準(zhǔn)來(lái)表示文本字符,這種方式實(shí)際上就是用一個(gè)唯一的單字節(jié)大小的整數(shù)值來(lái)表示每個(gè)字符。
像hello.c這樣只由ASCII字符構(gòu)成的文件稱為文本文件,所有其他文件都稱為二進(jìn)制文件。
系統(tǒng)的所有信息——包括磁盤(pán)文件,存儲(chǔ)器中的程序、存儲(chǔ)器中存放的用戶數(shù)據(jù)以及網(wǎng)絡(luò)上傳送的數(shù)據(jù),都是由一連串位表示的。區(qū)分不同數(shù)據(jù)對(duì)象的唯一方法是我們讀到這些數(shù)據(jù)對(duì)象時(shí)的上下文。比如,在不同的上下文中,一個(gè)同樣的字節(jié)序列可能表示一個(gè)整數(shù)、浮點(diǎn)數(shù)、字符串或者機(jī)器指令。
C語(yǔ)言與Unix操作系統(tǒng)關(guān)系密切。Unix幾乎全部都是用C編寫(xiě)的,所以可以很方便地移植到新的機(jī)器上,這種特點(diǎn)為C和Unix贏得了更為廣泛的支持。C語(yǔ)言的指針是造成困惑和程序錯(cuò)誤的一個(gè)常見(jiàn)原因。同時(shí),C還缺乏對(duì)非常有用的抽象(例如類、對(duì)象和異常)的顯式支持。Cpp和Java這樣針對(duì)應(yīng)用級(jí)程序的新程序設(shè)計(jì)語(yǔ)言解決了這些問(wèn)題。
hello程序的生命周期是從一個(gè)高級(jí)C語(yǔ)言程序開(kāi)始的,因?yàn)檫@種形式能夠被人讀懂。然后,為了在系統(tǒng)上運(yùn)行hello.c,每條C語(yǔ)句都必須被其他程序轉(zhuǎn)化為一系列的低級(jí)機(jī)器語(yǔ)言指令。然后這些指令按照一種可執(zhí)行目標(biāo)程序的格式打好包,并以二進(jìn)制磁盤(pán)文件的形式存放起來(lái)。目標(biāo)程序也稱為可執(zhí)行目標(biāo)文件。
unix> gcc -o hello hello.c
預(yù)處理階段-預(yù)處理器(cpp)根據(jù)以字符#開(kāi)頭的命令,修改C的原始程序。#include<stdio.h>告訴預(yù)處理器讀取系統(tǒng)頭文件stdio.h的內(nèi)容,并把它插入到程序文本中,結(jié)果得到另一個(gè)C程序,通常以.i作為文件擴(kuò)展名。
編譯階段-編譯器(ccl)將hello.i轉(zhuǎn)換為匯編語(yǔ)言程序。不同語(yǔ)言編譯器產(chǎn)生的輸出文件用的都是一樣的匯編語(yǔ)言。
匯編階段-匯編器(as)將hello.s翻譯成機(jī)器語(yǔ)言指令,把這些指令打包成可重定位目標(biāo)程序(relocatable object program)的格式,并將結(jié)果保存在hello.o中,一個(gè)二進(jìn)制文件,其字節(jié)編碼是機(jī)器指令而不是字符,如果在文本編輯器中打開(kāi)hello.o,看到的將是一堆亂碼。
鏈接階段-printf函數(shù)存在于一個(gè)名為printf.o的單獨(dú)的預(yù)編譯好了的目標(biāo)文件中,而這個(gè)文件必須以某種方式合并到hello.o程序中。鏈接器(ld)就負(fù)責(zé)處理這種合并。結(jié)果得到hello文件,一個(gè)可執(zhí)行目標(biāo)文件,可以被加載在內(nèi)存中,由系統(tǒng)執(zhí)行。
unix> ./hello
Shell(外殼)是一個(gè)命令行解釋器,它輸入一個(gè)提示符,等待你輸入一個(gè)命令行,然后執(zhí)行這個(gè)命令。如果該命令行的第一個(gè)單詞不是一個(gè)內(nèi)置的外殼命令,那么外殼就會(huì)假設(shè)這是一個(gè)可執(zhí)行文件的名字,它將加載并運(yùn)行這個(gè)文件。
系統(tǒng)硬件組成
1.總線:貫穿整個(gè)系統(tǒng)的一組電子管道。攜帶信息字節(jié)并負(fù)責(zé)在各個(gè)部門(mén)間傳遞。通??偩€被設(shè)計(jì)成傳送定長(zhǎng)的字節(jié)塊,也就是字(word)。字中的字節(jié)數(shù)(即字長(zhǎng))是一個(gè)基本的系統(tǒng)參數(shù),各個(gè)系統(tǒng)不盡相同,大多數(shù)是四個(gè)字節(jié)(32位)/八個(gè)字節(jié)(64位)。為討論方便,假設(shè)字長(zhǎng)為4個(gè)字節(jié),并且總線每次只傳送一個(gè)字。
2.I/O設(shè)備:輸入/輸出設(shè)備是系統(tǒng)與外部世界的聯(lián)系通道。示例中的包括——作為用戶輸入的鍵盤(pán)和鼠標(biāo),作為用戶輸出的顯示器,以及用于長(zhǎng)期存儲(chǔ)數(shù)據(jù)和程序的磁盤(pán)驅(qū)動(dòng)器(即磁盤(pán))。最初,可執(zhí)行程序hello就存放在磁盤(pán)上。每個(gè)I/O設(shè)備都通過(guò)一個(gè)控制器或適配器與I/O總線相連??刂破骱瓦m配器之間的區(qū)別主要在于其封裝方式。控制器是置于I/O設(shè)備本身的或者系統(tǒng)的主印制電路板(主板)上的芯片組,而適配器則是一塊插在主板插槽上的卡。其功能都是在I/O總線和I/O設(shè)備之間傳遞信息。
3.主存:一個(gè)臨時(shí)存儲(chǔ)設(shè)備。在處理器執(zhí)行程序時(shí),用來(lái)存放程序和程序處理的數(shù)據(jù)。物理上說(shuō),是由一組動(dòng)態(tài)隨機(jī)存取存儲(chǔ)器DRAM芯片組成的。邏輯上說(shuō),存儲(chǔ)器是一個(gè)線性數(shù)組,每個(gè)字節(jié)都有其唯一的地址(即數(shù)組索引),這些地址從0開(kāi)始。一般而言,組成程序的每條機(jī)器指令都由不同數(shù)量的字節(jié)構(gòu)成。與C程序變量相對(duì)應(yīng)的數(shù)據(jù)項(xiàng)的大小是根據(jù)類型變化的。例如,在運(yùn)行Linux的IA32機(jī)器上,short類型的數(shù)據(jù)需要2個(gè)字節(jié),int、float和long類型需要4個(gè)字節(jié),double類型需要8個(gè)字節(jié)。
4.處理器(CPU):即中央處理單元,是解釋(或執(zhí)行)存儲(chǔ)在主存中指令的引擎。處理器的核心是一個(gè)字長(zhǎng)的存儲(chǔ)設(shè)備(或寄存器),稱為程序計(jì)數(shù)器(PC)。在任何時(shí)刻,PC都指向主存中的某條機(jī)器語(yǔ)言指令(即含有該條指令的地址)。從系統(tǒng)通電開(kāi)始直到斷電,處理器一直在不斷執(zhí)行PC指向的指令,再更新PC,使其指向下一條指令。處理器按照非常簡(jiǎn)單的指令執(zhí)行模型來(lái)操作,這個(gè)模型是由指令集結(jié)構(gòu)決定的。在這個(gè)模型中,指令按照嚴(yán)格的順序執(zhí)行,而執(zhí)行一條指令包含一系列的步驟。處理器按照PC指向的存儲(chǔ)器處讀取指令,解釋指令中的位,執(zhí)行該指令指示的簡(jiǎn)單操作,然后更新PC,而這條指令并不一定和存儲(chǔ)器中剛剛執(zhí)行的指令相鄰。然而并非所有操作都是簡(jiǎn)單如此,大多數(shù)操作圍繞著主存、寄存器文件(register file)和算數(shù)/邏輯單元(ALU)進(jìn)行。寄存器文件是一個(gè)小的存儲(chǔ)設(shè)備,由一些1字長(zhǎng)的寄存器組成,每個(gè)寄存器都有唯一的名字。ALU計(jì)算新的數(shù)據(jù)和地址值。一些操作如加載(把一個(gè)字節(jié)/字從主存復(fù)制到寄存器,以覆蓋寄存器原來(lái)的內(nèi)容),存儲(chǔ)(把一個(gè)字節(jié)/字從寄存器復(fù)制到主存的某個(gè)位置,以覆蓋這個(gè)位置原來(lái)的內(nèi)容),操作(把兩個(gè)寄存器的內(nèi)容復(fù)制到ALU,令A(yù)LU對(duì)這兩個(gè)字做算術(shù)操作,并將結(jié)果存放到一個(gè)寄存器中覆蓋原先內(nèi)容),跳轉(zhuǎn)(從指令本身抽取一個(gè)字,并將這個(gè)字復(fù)制到PC中,以覆蓋PC中原來(lái)的值)。指令集結(jié)構(gòu)描述的是每條極其代碼指令的結(jié)果,而微體系結(jié)構(gòu)描述的是處理器實(shí)際上如何實(shí)現(xiàn)。
高速緩存存儲(chǔ)器:為解決處理器和主存之間的差異所用的更小更快的存儲(chǔ)設(shè)備所用的,作為暫時(shí)的集結(jié)區(qū)域,以存放處理器近期可能會(huì)需要的信息。達(dá)到數(shù)萬(wàn)字節(jié)的L1高速緩存,訪問(wèn)速度幾乎和訪問(wèn)寄存器一樣快,其使用的是靜態(tài)隨機(jī)訪問(wèn)存儲(chǔ)器(SRAM)的硬件技術(shù)實(shí)現(xiàn)的。
操作系統(tǒng):可以看作是應(yīng)用程序和硬件之間的一層軟件。所有應(yīng)用程序?qū)τ布牟僮鲊L試都必須通過(guò)操作系統(tǒng)。操作系統(tǒng)的基本功能1)防止硬件被失控的應(yīng)用程序?yàn)E用;2)向應(yīng)用程序提供簡(jiǎn)單一致的機(jī)制來(lái)控制復(fù)雜又通常大相徑庭的低級(jí)硬件設(shè)備。
進(jìn)程:操作系統(tǒng)對(duì)一個(gè)正在運(yùn)行的程序的一種抽象。在一個(gè)系統(tǒng)上可以同時(shí)運(yùn)行多個(gè)進(jìn)程,而每個(gè)進(jìn)程都好像在獨(dú)占地使用硬件。而并發(fā)運(yùn)行,是指一個(gè)進(jìn)程的指令和另一個(gè)進(jìn)程的指令是交錯(cuò)執(zhí)行的。傳統(tǒng)系統(tǒng)在一個(gè)時(shí)刻只能執(zhí)行一個(gè)程序,而先進(jìn)的多核處理器可以同時(shí)執(zhí)行多個(gè)程序。無(wú)論是單核還是多核系統(tǒng)中,一個(gè)CPU看上去都像是在并發(fā)地執(zhí)行多個(gè)進(jìn)程,這是通過(guò)處理器在進(jìn)程間切換實(shí)現(xiàn)的。操作系統(tǒng)實(shí)現(xiàn)這種交錯(cuò)執(zhí)行的機(jī)制稱為上下文切換。操作系統(tǒng)保持跟蹤進(jìn)程運(yùn)行所需的所有狀態(tài)信息,這種狀態(tài)即上下文,包括許多信息,如PC和寄存器文件的當(dāng)前值,以及主存的內(nèi)容。
線程:盡管通常我們認(rèn)為進(jìn)程只有單一的控制流,但在現(xiàn)代系統(tǒng)中,一個(gè)進(jìn)程實(shí)際上可以由多個(gè)稱為線程的執(zhí)行單元組成,每個(gè)線程都運(yùn)行在進(jìn)程的上下文中,并共享同樣的代碼和全局?jǐn)?shù)據(jù)。由于網(wǎng)絡(luò)服務(wù)器對(duì)并行處理的需求,線程成為越來(lái)越重要的編程模型,因?yàn)槎嗑€程之間比多進(jìn)程間更容易共享數(shù)據(jù),也因?yàn)榫€程一般都比進(jìn)程更高效。當(dāng)有多處理器可用的時(shí)候,多線程也是一種使程序可以更快運(yùn)行的辦法。
虛擬存儲(chǔ)器:是一個(gè)抽象概念,它為每個(gè)進(jìn)程提供了一個(gè)假象,即每個(gè)進(jìn)程都在獨(dú)占地使用內(nèi)存。每個(gè)進(jìn)程看到的是一致的存儲(chǔ)器,稱為虛擬地址空間。Linux中,地址空間最上面的區(qū)域是為操作系統(tǒng)中的代碼和數(shù)據(jù)保留的,這對(duì)所有進(jìn)程都是一樣的。地址空間的底部區(qū)域存放用戶進(jìn)程定義的代碼和數(shù)據(jù)。
程序代碼和數(shù)據(jù):對(duì)所有進(jìn)程來(lái)說(shuō),代碼是從同一固定地址開(kāi)始,緊接著的是和C全局變量相對(duì)應(yīng)的數(shù)據(jù)位置。代碼和數(shù)據(jù)區(qū)是直接按照可執(zhí)行目標(biāo)文件的內(nèi)容初始化的,在示例中就是可執(zhí)行文件hello。
堆:代碼和數(shù)據(jù)區(qū)后緊隨著的是運(yùn)行時(shí)堆。代碼和數(shù)據(jù)區(qū)在一開(kāi)始運(yùn)行時(shí)就被規(guī)定了大小,與此不同,當(dāng)調(diào)用如malloc和free這樣的C標(biāo)準(zhǔn)庫(kù)函數(shù)時(shí),堆可以在運(yùn)行時(shí)動(dòng)態(tài)地?cái)U(kuò)展和收縮。
共享庫(kù):在地址空間約中間部分是一塊用于存放像C標(biāo)準(zhǔn)庫(kù)和數(shù)學(xué)庫(kù)這樣共享庫(kù)代碼和數(shù)據(jù)的區(qū)域。
棧:用于編譯器實(shí)現(xiàn)函數(shù)調(diào)用。和堆一樣在程序執(zhí)行期間可動(dòng)態(tài)擴(kuò)展和收縮。每次調(diào)用一個(gè)函數(shù)時(shí),棧會(huì)增長(zhǎng)。從一個(gè)函數(shù)返回時(shí),棧會(huì)收縮。
內(nèi)核虛擬存儲(chǔ)器:內(nèi)核總是駐留在內(nèi)存中,是操作系統(tǒng)的一部分。地址空間頂部區(qū)域是為內(nèi)核保留的,不允許應(yīng)用程序讀寫(xiě)這個(gè)區(qū)域的內(nèi)容或直接調(diào)用內(nèi)核代碼定義的函數(shù)。
文件:即字節(jié)序列。每個(gè)I/O設(shè)備,包括磁盤(pán)、鍵盤(pán)、顯示器、網(wǎng)絡(luò),都可視為文件。系統(tǒng)中所有輸入輸出都是通過(guò)使用一小組稱為Unix I/O的系統(tǒng)函數(shù)調(diào)用讀寫(xiě)文件實(shí)現(xiàn)的。
并發(fā)(concurrency)和并行(parallelism):并發(fā)指一個(gè)同時(shí)具有多個(gè)活動(dòng)的系統(tǒng),并行指的是用并發(fā)使一個(gè)系統(tǒng)運(yùn)行更快。
超線程(hyperthreading):有時(shí)稱為同時(shí)多線程(simultaneous multi-threading),是一項(xiàng)允許一個(gè)CPU執(zhí)行多個(gè)控制流的技術(shù)。涉及CPU某些硬件有多個(gè)備份,比如程序計(jì)數(shù)器和寄存器文件,而其它硬件部分只有一份,如執(zhí)行浮點(diǎn)算術(shù)運(yùn)算的單元。常規(guī)處理器需要約20000個(gè)時(shí)鐘周期做不同線程間的轉(zhuǎn)換,而超線程的處理器可以在單個(gè)周期的基礎(chǔ)上決定要執(zhí)行哪一個(gè)線程,這使得CPU能夠更好地利用它的處理資源。
指令集并行:在較低的抽象層次上,現(xiàn)代處理器可以同時(shí)執(zhí)行多條指令的屬性。如果處理器可以達(dá)到比一個(gè)周期一條指令更快的執(zhí)行效率,就稱之為超標(biāo)量(superscaler)處理器。
單指令、多數(shù)據(jù)并行:在最低層次上,許多現(xiàn)代處理器擁有特殊的硬件,允許一條指令產(chǎn)生多個(gè)可以并行執(zhí)行的操作,即SIMD并行。提供SIMD指令多是為了提高處理影像、聲音和視頻數(shù)據(jù)應(yīng)用的執(zhí)行速度。雖然有些編譯器試圖從C程序中自動(dòng)抽取SIMD并行性,但更可靠的方法是使用編譯器支持的特殊向量數(shù)據(jù)類型來(lái)寫(xiě)程序。