深入理解計算機系統筆記_第1頁
深入理解計算機系統筆記_第2頁
深入理解計算機系統筆記_第3頁
深入理解計算機系統筆記_第4頁
深入理解計算機系統筆記_第5頁
已閱讀5頁,還剩75頁未讀, 繼續免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

深入理解計算機系統

(1)對于一個無符號數字x,截斷它到k位的結果就相當于計算xmod2”.

(2)在大多數的機器上,整數乘法指令相當地慢,需要12或者更多的始終周期,然而其他整數

運算一例如加法、減法、位移運算和移位一只需要1個時鐘周期.因此,編譯器使用的一項重

要的優化就是試著使用移位和加法運算的組合來代替乘以常數因子的乘法.

(3)在大多數的機器上,整數除法要比整數乘法更慢一需要30或者更多的始終周期.除以2

的哥也可以用移位運算來實現,只不過我們用的是右移,而不是左移.對于無符號和二進制補

碼數,分別使用邏輯移位和算術移位來到達目的.

1.注意系統的分類:主流的IA32(也就是X86),以及x86-64(也就是x64),還有種Intel的與原32位系統不兼容的

IA64.:

2.編譯系統由預處理器,編譯器,匯編器和鏈接器組成。

3.單指令多數據并行稱為SIMD并行,其擴展為SSE指令集。

4.x64上long為8字節,指針也為8字節。

5.無符號數右移必須采用邏輯右移,而有符號數一?般采用算術右移.

6.有符號數遇見無符號數會默認強轉為無符號數。

7.short轉為unsigned時,是先擴展大小再符號轉換。

8.補碼非的計算:從左到右將第一個為1的位前的所有位取反。

9.負數的補碼移位向下舍入。

10.正浮點數能使用整數排序函數來進行排序。

11.浮點加法和乘法不具備結合性,浮點乘法在加法上不具備分配性。

12.預處理器擴展源代碼,然后編譯器生成源代碼的文本匯編代碼,匯編器轉成二進制匯編碼,鏈接器生成exe

或dll或lib。

13.存放器可以保存地址也可以保存值。注意匯編中的加括號表示為取該地址指向的值,如(%eax)指%eax中保

存的地址指向的值。

14.傳送指令的兩個操作符不能都指向存儲器。

15.棧指針%esp保存著棧頂元素的值,%eax保存函數返回值。

16.棧從高地址往低地址分配,堆從低地址往高地址分配°

17.注意:lea,假設為Ieal7(%edx,%eax,4),那么當%edx中保存的是地址時,lea為取有效地址,而當%edx

中保存的是值時,lea為算術運算,即7+%edx+%eax*4。這兒的%eax總保存值。說白了,其實lea一直是在

做計算,只是%edx影響了直觀表達而已。

18.注意:處理有無符號值的操作是通過不同的匯編指令來區分的。

19.大多數匯編器根據一個循環的do-while形式來產生循環代碼,逆向工程會用到。

20.指令無視操作數的長度。

21.因為有個條件傳送的優化策略,所以(xp?*xp:0)這條語句其實兩個選擇分支都會執行。

22.32位系統中,大多數棧中信息的訪問其位置都是基于幀指針的。而64位系統中,棧的存儲信息數已被弱化,

所以無幀指針/,

23.訪問某個局部變量的前提是該局部變量至少有個可引用的地址,所以局部變量被保存在了棧中。

24.為了防止從效率低的存儲器讀與值時,可能因數據未對齊而造成屢次讀寫從而導致低性能,IA32要求數據一

定要對齊。編譯器在編譯肘會強制對齊。

25.匯編指令leave等于倆pop,效果?樣,選擇隨意。pop和push在棧上分配空間的方式是直接棧指針減去或

加上偏移量。

26.指針之差等于相差字節數/所指類型大小字節數。

27.存放器不夠用時會出現存放福溢出,這時就必須有值被保存在棧卜了,一般是將只讀變量放入棧.

28.GCC會對局部char類型的緩沖區插入金絲雀保護代碼。

29.在C中內聯匯編代碼只能針對某?類機2〉

30.SSE2引入浮點數面向存放器的指令集,而不用基于棧的方法。

31.X64能讓匯編代碼比X32少很多,但是實際性能提升不會很大。但是從改良上來說性能應該提升很大很大啊,

為什么。。。難道是x32已經被優化的變態了???

32.注意rep有時當空操作使。

33*64中,枝空間向下128字節以內的區域仍可以被函數訪問,該區域被ABI稱為紅色地帶。

34.浮點相關:把存儲模型,指令和傳遞規那么組合稱為浮點體系結構。

35.邏輯門只是簡單的響應輸入的變化而已。

36.HCL中,上只是表示用一個名字來稱謂一個表達式。其類switch的表達中的”1.“等同于switch中的Default

Case。

37.存放器文件上的讀或寫端口都分別成對,一個傳ID,一個傳內容。

38.訪存階段讀寫存儲器,寫回階段將結果寫到存放器文件。

39.存放器文件和數據存儲器等都是當前時鐘隨意讀,下一時鐘統一寫入更新。即時鐘控制狀態元素的更新。

40.流水線即保持各單元在時鐘周期內忙碌不已,一套流水線硬件供多個流水線使用。

41.加載互鎖和數據轉發技術結合起來足以處理可能類型的數據冒險。

42.當流水線化的系統中出現多條指令引起的異常時,最深的指令被處理的優先級最高。

43.每個時鐘周期執行多個操作稱為超標量,而超線程是指一個核同時運行倆線程。

44.處理器功能單元的性能表示中,延遲指按照嚴格順序執行完成合并運算所需要的最小周期數,而吞吐量指理

論上最快完成一個操作所需周期數,

45.使用SSE可以降低吞吐量界限。

46.書寫適合條件傳送實現的”功能式“代碼。

47.每個加載/存儲單元每個時鐘周期只能啟動一條加載/存儲操作。

48.性能提高技術:①.采用適宜的算法和數據結構。②.消除連續的函數循環調用,在可能時盡量將計算移到循環

外:消除不必要的存儲器引用,引入臨時變量來保存中間結果;保持內層循環在存儲器層面的局部性。③展開循環;

通過多個累計變量和重新結合等技術,提高指令級并行:用功能的風格重寫條件操作,使得編譯采用條件數據傳送。

49.內存是硬盤的緩存。

50.corei7上所有的SRAM高速緩存存儲器都在CPU芯片上。

51.對丁性能來說,存儲淵訪問總數和不命中率相比,不命中率影響要更大。估”是因為存儲器的寫何緩存機制。

52.存儲器性能注意:將注意力集中在內循環上,大局部計算和存儲器訪問都發生在這里:通過按照數據對象存

儲在存儲器中的順序,以步長為1來讀數據,從而使得空間局部性最大;一旦從存儲器中讀入了一個數據對象,

就盡可能多的使用它,從而使得時間局部性最大。

53.對于靜態庫的鏈接,只會鏈接程序中用到的該庫(.lib)中的模塊(.obj),這其實也解釋為什么分別鏈接靜態庫版

本和動態庫版本的兩程序大小相差不是那么懸殊。

54.當前指令處理完后,處理器才能去發現中斷是否發生。

55.Linux系統調用的參數都是通過通用存放淵而不是棧傳遞的。

56.C++的try-catch是C種setjmp和longjmp的更加結構化的版本。

57.DRAM作為磁盤的緩存,不命中開銷巨大。

58.磁盤上的交換文件同時乂作為DRAM保存數據的緩存。

59.延遲私有對象中的拷貝最充分的利用了稀有的物理存儲器,這是通過寫時拷貝實現的。

60.一個系統中被所有進程分配的虛擬存儲器的全部數量是受磁盤上交換空間的數量限制的.

61.造成堆利用率很低的主要原因是內存碎片的存在。

62.有時為了極個別的幾個特殊情,兄而要去每次調用時都檢查,還不如通過某些方式直接把特殊情況一般化,能

用通用的方式去處理。

63.內存引用導致的崩潰要注意;引用壞指針/野指針,以及讀未初始化的存儲器。

64.UNIX信號是不排隊的,假設為考慮處理,那么會直接丟棄。

65.函數內部的static變量也是線程間共享的。

66.注意并行和并發的區別,并行程序是一個運行在多個處理器上的并發程序。

67.線程數多過核數對效率反而會有影響,但影響不大。

68.注意:rand和ctime,localtime等函數時線程不平安的,慎用啊慎用!可用其可重入版本。

69.互斥鎖記得相同順序加鎖解鎖,

70.包裝錯誤處理函數,是一個非常不錯的做法。

//============================================

2010.07.08

深入理解計算機系統

(1)反匯編器一些特性說明:

1)IA32指令長度從1?15個字節不等.指令編碼被設計成使常用的指令以及操作較少的指令所需的字節

數少,二那些不太常用或操作數較多的指令所需字節數較多.

2)指令格式是按照這樣一種方式設計的,從某個給定位置開始,可以將字節唯一地解碼成機器指令.例如,

只有指令pushl%ebp是以字節值55開頭的.

3:i反匯編器只是根據目標文件中的字節序列來確定匯編代碼的.它不需要訪問程序的源代碼或匯編代碼.

4)反匯編器使用的指令命名規那么與GAS(GnuASembler)使用的有些細微的差異.

5j與codes中的匯編代碼相比,我們發現結尾多了一條nop指令.這條指令板本不會被執行(它在過程返

回指令之后),即使執行了也不會有任何影響(所以稱之為nop,是“n。operation"的簡寫,同城讀作"noop").

編譯那插入這樣的指令是為了填充存儲該過程的空間.

(2)IA32加了?條限制,傳送指令的兩個操作數不能都指向存儲器位置.將?個值從?個存儲器位置拷到

另一個存儲器位置需要兩條指令一第一條指令將源值加載到存放器值寫入目的位置.

(4)根據慣例,所有返回真書或指針值的函數都是通過將結果放在存放器%eax中來到達目的的.

⑸加載有效地址(Loadeffectiveaddress)指令leal實際上是movl指令的變形.它的指令形式是從存儲

器讀取數據到存放器,但實際上它根本就沒有引用存儲器.它的第一個操作數看上去是一個存儲器引用,

但該指令并不是從指定的位置讀入數據,而是將有效地址寫入到目的操作數(如存放器).

(6)一元操作,只有一個操作數,既作源,也作口的.這個操作數可以是一個存放器,也可以是一個存儲器位

置.比方說incl(%esp)會是棧頂元素加1.這種語法讓人想起C口的加1運算符(++)和減1(-).

(7)二元操作,第二個操作數既是源乂是目的.這種語法讓人想起C中向+=這樣的賦值運算符.不過要注意,

源操作數是第一個,目的操作數是第二個,這是不可交換操作特有的.例如,指令subl%eax,%edx使存放

器%0<^<的值減去%eax中的值第一個操作數可以是立即數、存放器或存儲器位置.第二個操作數可以是

存放器或是存儲器位置.不過同movl指令一樣,兩個操作數不能同時都是存儲器位置.

(8)divi指令執行無符號除法,通常會事先將存放器%3(^設置為0.

//============================================

2010.07.09

深入理解計算機系統

(1)匯編語言中,直接跳轉時給出一個標號作為跳轉H標地;間接跳轉的寫法是…后面跟一個操作數指示

符.如

jmp*%eax表示用存放器%eax中的值作為跳轉目標;

jmp*(%eax)表示已%eax中的值作為讀地址,從存儲器中讀出跳轉目標;

(2)call指令有一個目標,指明被調用過程起始的指令地址.同跳轉一樣,調用可以是直接的,也可以是間接

的.在匯編代碼中,直接調用的目標是?個標號,而間接調用的目標是*后面跟?個操作數指示符,其語法與

rrovel指令的操作數的語法相同.

⑶call指令的效果是將返回地址入棧,并跳轉到被調用過程的起始處.返回地址是緊跟在程序中call后面

的那條指令的地址.這樣當被調用過程返回時,執行會從此繼續指令從棧中彈出地址,并跳轉到那個位

置.要正確使用這條指令,就要使棧準備好,棧指針要指向前面call指令存儲返回地址的位置.leave指令可

以用來使棧做好返回的準備.它等價于下面的代碼序列:

rrovl%ebp,%esp//setstackpointertobeginningofframe

popl%ebp//restoresaved%ebpandsetstackptrtoendofcall'sframe

另外這樣準備工作也可以通過直接使用傳送和彈出操作來完成.

存放器%eax可以用來返回值,如果函數要返回整數或指針的話.

(4)根據慣例,存放器%eax,%edx,和%ecx被劃分為調用者俁存(callersave)存放器.當過程p調用Q

時,Q可以覆蓋這些存放器,而不會被破壞任何P所需要的數據.

另外,存放器%ebx,%esi和%81被劃分為被調用者保存(calleesave)存放器.這意味著Q必須在覆蓋他

們之前,將這些存放器的值保存到棧中,并在返回前恢復他們,應為P(或某個更高層次的過程)可能會在今

后的計算中需要這些值.此外,根據這里描述的慣例,必須保持存放器%ebp和%esp.

(5)單操作數的操作符&和*可以產生指針和間接引用指針.也就是,對于一個表示某個對象的表達式

Expr,&Expr表示一個地址.對于表示一個地址的表達式Addr-Expr,*Addr-Expr表示該地址中的值.因此,

表達式Expr與*&Expr是等價的.

可以對數組和指針應用數組下標操作,如數組引用A[i]與表達式,(A+i)是一樣的.它計算第i個數組元素的

地址,然后訪問這個存儲器位置.

(6)數組元素在存儲器中是按照"行優先"的順序排列的,這就意味著先是行0的所有元素,后面是行1的所

有元素,以此類推.

(7)一個聯合的總的大小等于它最大域的大小.

(8)無論數據是否對齊JA32硬件都能正確工作.不過Jntel還是建議要對齊數據以提高存儲器系統的性

能.Linux沿用的對齊策略是2字節數據類型(例如short)的地址必須是2的倍數,而較大的數據烈性(例如

int,int*,float和double)的地址必須是4的倍數.注意,這個要求就意味著一個short類型對象的地址最低

位必須等于。.類似地,任何int類型的對象或指針的地址的最低兩位必須都是0.

//============================================

2010.07.13

深入理解計算機系統

(1)編寫高效程序需要兩類活動:

第一、我們必須選擇一組最好的算法和數據結構;

第二、我們必須編寫出編譯器能夠有效優化以轉換成高效可執行代碼的源代碼.

(2)優化程序性能的根本策略:

1:|高級設計.為手邊的問題懸著適當的算法和數據結構.要特別警覺,防止使用會漸進的產生糟糕性能的

算法或編碼技術.

2:i根本編碼原那么.防止限制優化的因素,這樣編譯器就能產生高效的代碼.

消除連續的函數調用.在可能時,將計算移到循環之外.考慮有選擇的妥辦程序的模塊性以獲得更大的效

率.

消除不必要的存儲器引用.引入噴時變量來保存中間結果.只有在最后的值計算出來時,才將結果存放到

數組或全局變量中.

3)低級優化.

嘗試各種與數組代碼相對的指針形式.

通過展開循環降低循環開銷.

通過諸如迭代分隔之類的技術,找到使用流水線的功能單元的方法.

最后的忠告,要小心防止花費精力在令人位家的結果上.?項有用的技術是.在優化代碼時使用檢查代碼

(checkingcode)來測試代碼的每個版本,以確保在這一過程中沒有引入錯誤,檢瓷代碼將一系列測試應用

到程序上,確保它得到期望的結果.

(3)量化評價一個程序中局限性的簡單原那么:

重復引用同一個變量的程序有良好的時間局限性.

對于具有步長為k的引用模式的程序,步長越小,空間局限性越好.具有步長為1的引用模式的程序有很好

的空間局限性.在存儲器中以大步長跳來跳去的程序空間局限性會很差.

對于取指令來說,循環有好的時間和空間局限性.循環體越小,循環迭代次數越多,局部性越好.

(4)推薦以下技術:

符你的注意力集中在內部循環上,大局部計算和存儲器訪問都發生在這里.

通過按照數據對象存儲在存儲器中的順序來讀取數據數據,從而使得你的程序中的空間局部性最大.

記住,不明中率只是確定你代碼性能的一個因素(雖然是重要的).存儲器訪問數量也扮演著重要角色,有

時需要在兩者之間做一下折中.

〃============================================

2010.07.19

深入理解計算機系統

(1)學習鏈接只是的原因:

理解連及其將幫助你構造大型程序.

理解連接將幫助你防止一些危險的編程錯誤.

理解連將幫助你理解其他重要的系統概念.

理解鏈接將使你能夠開發共享庫.

(2)一個典型的ELF可重定位目標文件包含下面兒個節:

.text:以編譯程序的機器代碼.

.rodata:只讀數據,如printf語句口的格式串和開關(switch)語句的跳轉表.

.data:以初始化的全局c變量.局部C變量在運行時被保存在棧中,既不出現在.data中,也不出現在.bss節

中.

.bss:未初始化的全局變量.在口標文件中這個節不占據實際的空間,它僅僅是一個占位符.口標文件格式

區分初始化和未初始化變量是為了空間效率:在目標文件中,未初始化變量不需要占據任何實際的磁盤空

間.

.symtab:一個符號表(symboltable),他存放在程序中被定義和引用的函數和全局變量的信息.

?回.text:當連接器把目標文件和其他文件結合時,.text節中的許多位置都需要修改.一般而言,任何調用外

部函數或者引用全局變量的指令都需要修改.另一方面,調用本地函數的指令那么不需要修改.注意,可執

行目標文件中并不需要重定位信息,因此通常省略,除非使用者顯式地指示連接器包含這些信息.

.el.data:被模塊定義或引用的任何全局變量的信息.一般而言,任何已初始化全局變量的初始值是全局變

量或者外部定義函數的地址都需要被修改.

.Cebug:一個調試符號表,其中有些表目是程序中定義的局部變量和類型定義,有些表目是程序中定義和

引用的全局變量,有些是原始的C源代碼.只有以?g選項調用編譯驅動程序時,才會得到這張表.

.line:原始C源程序中的行號和.text節中機器指令之間的映射.只有以-g選項調用編譯驅動程序時才會得

到這張表.

.strtab:一個字符串表,其內容包括.systab和.debug節中的符號表,以及節頭部中的節名字.字符串表就是

以null結尾的字符串序列.

(3)利用static屬性隱藏變量和函數的名字.

C程序員使用static屬性在模塊內部隱藏變量和函數聲明,就像你在jave和C++中使用public和private

聲明一樣.C源代碼文件扮演模塊的角色.任何聲明帶有static屬性的全局變量或者函數都是模塊私有的.

類似的,任何聲明為不帶static屬性的全局變量和函數都是公開的,可以被其他模塊訪問.盡可能用static

書香來保護你的變量和函數時很好的編程習慣.

(4)函數和已經初始化的全局變量是強符號,未初始化的全局變量是弱符號.

(5)根據強弱符號的定義,unix連接器使用下面的規那么來處理多出定義的符號:

規那么1:不允許有多個強符號.

規那么2:如果有一個強符號和多個弱符號,那么選擇強符號.

規那么3:如果有多個弱符號,那么從這些弱符號中任意選擇一個.

〃============================================

2010.07.20

深入理解計算機系統

(1)共享庫(sharedlibrary)是致力于解決靜態庫缺陷的一個現代創新產物.

共享庫是一個目標模塊,在運行時,可以加載到任意的存儲器地址,并在存儲器中和一個程序連接起來,這

個過程稱為動態鏈接(dynamiclinking),是由一個叫動態連接器(dynamiclinker)的程序來執行的.

(2)共享庫的“共享”在兩個方面有所不同.首先,在任何給定的文件系統中,對于一個庫只有一個.s。文件.

所有引用該庫的可執行目標文件共享這個.so文件中的代碼與數據,而不像靜態庫的內容那樣被拷貝和嵌

入到引用它們的可執行的文件中.其次,在存儲器中,一個共享庫的.text節只有一個副本可以被不同的正

在運行的進程共享.

⑶java定義了一個標準調用規那么,叫做jave本地接口(javenativeinterface,JNI),它允許java程序調

用"本地的"c和C++函數,JNI的根本思想是將本地的C函數,比方說foo,編譯到共享庫中,如foo.so.3■

個長在運行的java程序試圖調月函數foo時,java解釋程序利用dlopen接口(或者某個類似于此的東西)

動態鏈接和加載foo.so,然后再調用函數too.

(4)理解ECF很重要的原因:

理解ECF將幫助你理解重要的系統概念.

理解ECF將幫助你理解應用程序是如何與操作系統交互的.

理解ECF將幫助你編寫有趣的新應用程序.

理解ECF將幫助你理解軟件異常如何工作.

(5)當異常處理程序完成處理后、根據引起一場的事件的類型,會發生一下三種情況中的一種:

1.處理程序將控制返回給當前指令Icurr(當事件發生時正在執行的指令).

2.處理程序將控制返回給Inext:如果沒有發生異常將會執行的下一條指令).

3.處理程序終止被中斷的程序.

(6)異常的類型有:中斷(interrupt),陷阱(trap),故障(fault)和終止(abort).

⑺任何邏輯流在時間上和另外的邏輯流重疊的進程被稱為并發進程(concurrentprocess).

而這兩個進程就被稱為并發運行.

進程和其他進程輪換運行的概念稱為多任務(multitasking).一個進程執行它的控制流的一局部的每一時

間段叫做時間片(timeslice).因此,多任務也叫做時間分片(timeslicing).

(8)進程的三種狀態:

運行.進程要么在CPU上執行,要么在等待被執行且最終會被調度.

暫停.進程的執行被掛起(suspended),且不會被調度.

終止.進程永遠地停止.進程因為三種原因終止:收到一個信號,該信號的默認行為是終止進程;從主程序返

回;調用exit函數.

(9)一個終止了但還未被回收的進程成為僵死進程(zombie).

//============================================

2010.07.21

深入理解計算機系統

(1)任意時刻,虛擬頁面的集合都分為三個不相交的子集:

未分配的:VM系統還未分配(或者創立)的頁.未分配的塊沒有任何數據和他們相關聯,因此也就不占用任

何磁盤空間.

級存的:當前緩存在物理存儲器中的已分配頁.

未緩存的:沒有緩存在物理存儲器中的已分配頁.

(2)顯示分配器的一些相當嚴格的約束條件:

處理任意請求序列.

立即響應請求.

只使用堆.

對齊塊(對齊要求).

不修改已分配的塊.

(3)與套接字限定相關的流限定:

限定一:輸入函數跟在輸出函數之后.如果中間沒有插入對fflush,fseek,fsetpos或者rewind的調用,一個

輸入函數不能跟在一個輸出函數之后flush函數清空與流相關的緩沖區.

限定二:輸入函數跟在輸入函數之后.如果中間么有插入對fflush,fseek,fsetpos或者rewind的調用,一

個輸出函數不能跟隨在一個輸入函數之后,除非該輸入函數遇到了一個文件結束.

對I/O流的第-個限定能夠通過采用在每個輸入操作前刷新緩沖區這樣的規那么來保證實現.

保證實現第二個限定的唯一方法是,對同一個翻開的套接字描述符翻開兩個流,一個用來讀,一個用來寫;

FILE*fpin,*fpout;

fpint=fopen(sockfd,"r");

fpout=fopen(sockfd,"w");

但是這樣做也有問題,因為它要求應用程序在兩個流上都要調用fclose,這樣才能釋放與每個流相關聯的

存儲儲資源,防止存儲器xielou(bd太齷齪了).

fclose(fpin);

fclose(fpout);

//============================================

2010.08.05

深入理解計算機系統

(1)四類線程不平安函數.

第一類:不保護共享變量的函數;

第二類:保持跨越多個調用的狀態的函數;

第三類:返回指向靜態變量的指針的函數;

第四類:調用線程不平安函數的函數;

⑵死鎖.

程序員使用p和v操作順序不當,以至兩個信號量的禁止區域(forbiddenregion)重疊.

重疊的禁止區域引起了一組稱為死鎖區域(deadlockregion)的狀態.

死鎖是一個相當困難的問題,因為它不總是可預測的.

(3)使用簡單而有效的規那么來防止死鎖.

互斥鎖加鎖順序規那么:如果對于程序中每對?互斥鎖(s,t),每個既包含s也包含t的線程都按照相同的

順序同時對他們加鎖,那么這個程序就是無死鎖的.

〈深入理解計算機系統〉筆記分享

第一章:計算機系統漫游這本書是為這樣一些程序員而寫的,他們希望通過了解這些部件如何工作以及如

何影響程序的準確性和性能,來提高自身的技能.

系統中所有的信息一包括磁盤文件,存儲器中的程序,存儲器中存放的用戶數據以及網絡上傳送的數據,都

是由一串比特表示的.

c語言是貝爾實驗室的DennisRitchie于1969T973年間創立的.美國國家標準化組織在1989年公布了

ANSIC的標準.該標準定義了C語言和一系列函數庫,即所謂的標準C庫.

c和unix操作系統關系密切.c從開始就是作為一種用于unix系統的程序語言開發出來的.unix內核的大

局部,以及所有它支持的工具和函數庫都是用c語言編寫的.

C是一個小而簡單的語言,C語言的設計是由一個人完成的,其結果就是這是一個簡潔明了,沒有什么冗贅

的設計.

c是為實踐目的設計的.c是設計用來實現unix操作系統的.它是系統編程的首選.同時它非常適用于應用

級程序的編寫.

預處理器,編譯器,匯編器和連接器一起構成.了編譯系統.

匯編語言是非常有用的,因為它為不同的高級語言的不同編譯器提供了通用的輸出語言.

GNU環境包括EMAC編譯器,GCC編譯器,GDB調試器,匯編器,連接器,處理二進制文件的工具以及其他

一些部件.

shell是一種命令行解釋器,它輸出一個提示符,等待你輸入一行命令,然后執行這個命令.如果該命令的第

一個單詞不是一個內置的shell命令,那么shell就會假設這是一個可執行文件的名字,要加載和執行該文

件.

每個I/O設備都是通過一個控制器或適配器于I/O總線連接起求的.控制器和適配器之間的區別主要在

它們的組成方式.控制器是I/O設備本身中或是主板上的芯片組,而適配器那么是一塊插在主板插槽上的

卡.

主存是臨時存儲設備,在處理器執行程序時,它被用來存放程序和程序處理的數據.物理上來說,主存是由

一組DRAM芯片組成的,邏輯上來說,存儲器是由一個線性的字節數組組成的.

中央處理單元簡稱處理器,是解釋存儲在主存中指令的引擎.處理器的核心是一個被稱為程序計數器的字

長大小的存儲設備.在任何一個時間點上,PC都指向主存中的某條機器語言指令.

從系統通電開始,直到系統斷電,處理器一直在不假思索的重復執行相同的根本任務:從程序計數器指向

的存儲器處讀取指令,解釋指令中的位,執行指令中的簡單操作,然后更新程序計數器指向下一條指令,而

這條指令并不一定在存儲器中和剛剛執行的指令相鄰.

利用稱為DMA(直接存儲器存?。┑募夹g,數據可以不通過處理器而直接從磁盤到達主存.

常字符串的顯示過程:存儲器到存放器文件,再從存放器文件到顯示設備.

?個典型的存放器文件只存儲幾百字節的信息,主存里可存放幾百萬字節.然而,處理器從存放器文件中

讀數據比從主存中讀取要快幾乎100倍.

L1和L2高速緩存是用一種叫做靜態隨機訪問存儲器的硬件技術實現的.

我們可以把操作系統看成是應用程序和硬件之間插入的一層軟件.

操作系統有兩個根本功能:防止硬件被失控的應用程序濫用;在控制復雜而又通常廣泛不同的低級硬件設

備方面,為應用程序提供簡單一致的方法.

程序在現代系統上運行時,操作系統會提供一種假象,就好似系統上只有這個程序是在運行的.這些假象

是通過進程的概念來實現的,進程是計算機科學中最重要和最成功的概念之一.

在現代系統中,一個進程實際上可以由多個稱為線程的執行單元組成.每個線程都運行在進程的上下文中,

并共享同樣的代碼和全局數據.

虛擬存儲器是一個抽象概念,它為每個進程提供了一個假象,好似每個進程都在獨占的使用主存.每個進

程看到的存儲器都是一致的.

每個進程看到的虛擬地址空間由大量準確定義的區組成,從最低的地址開始:程序代碼和數據(代碼和數

據區是由可執行目標文件直接初始化的);堆(作為調用像malloc和free這樣的c標準庫函數的結果,堆可

以在運行時動態的擴展和收縮);共享庫(共享庫的概念非常強大,但是也是個相當難懂的概念);棧(位于

用戶虛擬地址空間頂部的是用戶棧,編譯器用它來實現函數的調用);內核虛擬存儲器(內核是操作系統總

是駐留在存儲器中的局部).

虛擬存儲器的運作需要硬件和操作系統軟件之間的精密更雜的相互合作,包括對處理器生成的每個地址

的硬件翻譯.根本思想是把一個進程虛擬存儲器的內容存儲在磁盤上,然后用主存作為磁盤的高速緩存.

Linux逐漸開展成為一個技術和文化現象,通過和GNU工程的力量結合,Linux工程開展成為了一個完整

的,符合Posix標準的Unix操作系統的版本,包括內核和所有支撐的根底設施.

操作系統內核是應用程序和硬件之間的媒介,它提供三個根本的抽象概念:文件是I/O設備的抽象概念:

虛擬存儲器是對主存和磁盤的抽象概念:進程是處理器,支撐和I/O設備的抽象概念.〃

第二章:信息的表示和處理

2的n次方的二進制表示為1后面加n個o;

指針變量將用到機器的全字長Jongint*也是這樣;

可移植性的?個方面就是使程序對不同數據不敏感;

c標準對不同類型的數字范圍設置了下限,但是沒有上限;

存儲數據分大端法和小端法;

反匯編器是?種確定可執行程序文件所表示的指令序列的工具;

不同的機器使用不同的且不兼容的指令和編碼方式;

布爾的and和異或分別相當于模2的乘法和加法;

c標準沒有明確定義使用那種類型的右移;

c和C++中的有符號樹是默認的;

c的標準并沒有要求用二進制補嗎來表示有符號數;

c庫中的文件<limits.h>定義了一-組常量,來限定運行編譯器的這臺機器的不同整型數據的范圍;

printf沒有使用任何輸出變量類型的信息(how);

無符號和有符號數混合運算,將有符號數轉化為無符號數;

一種有名的用來執行二進制補區的非的技術是取反并加一;

編譯器用移位和加法運算的組合來代替乘以常數因子的乘法;

單精度浮點的符號,指數和有效數字分別是1,8,23.雙精度為1,11,52.有效數字的第一位總是1,因此我們不

需要表示它;

IEEE浮點格式定義了4種不同的舍入方式,默認是找到最接近的匹配.這四種方式為:向偶數舍入,向。舍

入,向上舍入,向下舍入;

浮點存放器使用一種特殊的8o位的擴展精度格式;

浮點數取亦就是簡單的將其符號位取反;

第三章:程序的機器級表示匯編程序員可以看到程序計數器,整數,浮點數存放器和條件碼存放器;

匯編代碼只是將存儲器看成是一個很大且按字節尋址的數組;

對標量數據類型,匯編代碼也不區分有符號和無符號數,不區分各種類型的指針,甚至不區分指針和整數;

程序存儲器包含程序的目標代碼,操作系統需要的一些信息,用來管理過程調用和返回的運行時棧,以及

用戶分配的存儲器塊;

IA32CPU有8個32位值的存放器,前6個是通用的,后2個保存棧指針和幀指針;

最頻繁使用的指令是執行數據傳送的指令;

局部變量通常是保存在存放器中;

除了右移操作,所有的指令都不區分有符號數和無符號數;

IA32程序用程序棧來支持過程的調用,棧用來傳遞過程參數,存儲返回信息,保存存放器以供以后的恢復

之用,以及用于本地存儲;

為單個過程分配的那局部棧稱為梭幀,它的最頂端是以兩個指針定界的;

存放器%eax可以用來返回值,如果函數要返【可整數或指針的話;

IA32中%02.%?(1*和%ecx被劃分為調用者保存存放器,其余三個為被調用者保存存放器;

IA32也仍然在不斷增加新的指令類,來支持處理多媒體應用的需要;

許多計算機系統要求某種數據類型的對象必須是某個值的k倍,這種對其限制簡化了處理器和存儲器系

統之間的接口硬件設計;

&操作符可以應用到任何左值類的C表達式上,包括變量,結構,聯合和數組元素;

用高級語言編寫的程序可在許多不同的機器上編譯執行,而匯編代碼是于特定機器密切相關的;

1997-PentiumII1999-Pentiumin2OO1-Pentium4;

美國商標局不允許用數字作為商標;

Intel現在稱其指令集為IA32;

匯編代碼非常接近于機器代碼;

匯編代碼只是將存儲器看成是一個很大的,按字節尋址的數組;

機器實際執行的程序只是對一系列指令進行編碼的字節序列,機器對產生這些指令的源代碼一無所知;

call過程調用leave為返回準備棧ret從過程調用中返回;

指針提供一種統一方式,能夠遠程訪問數據結構;

malloc函數返回一個通用指針;

指針也可以指向函數,這提供了一個很強大的存儲和傳遞代碼引用的功能(how);

數年來,AMD的策略一致是在技術上緊跟Intel后面,生產性能稍低但是價格更廉價的處理器;

浮點數使用?組完全不同的指令和存放器(相對整數來說);

IA32加了一條限制,傳送指令的兩個操作數不能都指向存儲器的位置,第一個是源操作數,第二個是目標

操作數;

局部變量通常是保存在存放器中的,這樣就能更快的訪問到它;

加載有效地址(loadeffectiveaddress)指令leal實際上是movl指令的變形,但H標操作數必須是一個存放

器;

編譯器產生的代碼中會用一個存放器存放多個程序值,還會在存放器之間傳送程序值;

匯編代碼提供了實現非順序控機流的較低層次的機制,這是通過借助條件碼存放器來完成的;

當執行PC相關的尋址時,程序計數器的值是跳轉指令后面的那條指令的地址,,而不是跳轉指令本身的地

址;

switch語句不僅提高了C代碼的可讀性,而且通過使用一種跳轉表的數據結構使得實現更加立效,跳轉表

是一個數組,表項i是一個代碼段的地址,這個代碼段實現的是些開關索引值等于i時程序應該采取的動作;

數據傳遞,局部變量的分配了釋放是通過操縱程序棧來實現的;

IA32程序用程序棧來支持過程調用;

當對一個局部變量使用地址操作符&的時候,該變量不能保存在存放器中;

call指令的效果是將返回地址入棧,并跳轉到被調用過程的起始處.返回地址是緊跟在程序中call后面的

那條指令的地址.ret指令從棧中彈出地址并跳轉到那個位置;

在較老的IA32處理器模型中,整數乘法指令要花費3。個時鐘周期,所以編譯器要盡可能的防上使用它,

而大多數新近的處理器模型中,乘法指令只需要3個時鐘周期,所以不一定會進行這樣的優化;

c和C++都要求程序顯式的用free函數來釋放已經分配的空間,在Java中,釋放是由運行時系統通過一個

稱為垃圾回收的進程自動完成的;

存放器溢出是IA32一個很常見的問題,因為處理器的存放器數量太少了;

結構的實現類似于數組的實現,因為結構的所有組成局部都存放在存儲器中連續的區域內,而指向結構的

指針就是結構的第一個字節的地址;

struct數據類型的構造函數是c提供的于C++和java對象最為接近的東西;

蠕蟲是這樣一種程序,它可以自己運行,并且能夠將一個完全有效的自己傳播到其他的機器上.與此相應

的,病毒是這樣一段代碼,它能將自己添加都包括操作系統在內的其他程序中,但是它不能獨立的運行;〃

(141516節未看)

第五章:優化程序性能編寫高效的程序需要兩類活動:第一,我們必須選擇一組最好的算法和數據結構;第

二,我們必須編寫出編譯器能夠有效優化以轉成高效可執行代碼的源代碼;

事實上,編譯器只能執行有限的程序轉換,而且阻礙優化的因素還會阻礙這種轉換,阻礙優化的因素就是

程序行為中那些嚴重依賴丁執行環境的方面;

編譯器優化程序的能力受幾個因素的限制,包括:要求它們絕不能改變正確的程序行為:它們對程序行為,

對使用它們的環境了解有限;需要很快的完成編譯工作;

編譯器必須假設不同的指針可能會指向存儲器中同一個位置,這造成了一個主要的阻礙優化的因素,這也

是可能嚴重限制編譯器產生優化代碼時機的程序的?個方面;

編譯器會假設最糟的情況,并保持所有的函數調用不變;

代碼移動的優化包括識別出要執行屢次但是計算結果不會改變的計算,因而我們可以將計算移動到代碼

前面的,不會被屢次求值的局部;

c中的字符串是以null結尾的字符序列,strlen必須一步一步的檢查這個序列,直到遇到null字符;

過程的調用會帶來相當大的開銷,而且阻礙大多數形式的程序優化;

消除不必要的存儲器引用,具體是用一些臨時變量,這些變量很可能被存在存放器中(如果沒有溢出的話);

在匯編代碼級,看上去似乎是一次是執行一條指令,每條指令都包括從存放器或存儲器取值,執行一個操

作,并把結果存回到一個存放器或存儲器位置.在實際的處理器中,是同時對多條指令求值的.在某些設計

中,可以有8o或更多條指令在處理中.

P6微體系結構是自20世紀9。年代后期以來許多廠商生.產的高端處理器的典型.在工業界稱為超標量,

意思是它可以在每個時鐘周期執行多個操作,而且是亂序的,整個設計有兩個主要局部:ICU(Instruction

ControlUnit,指令控制單元)和EU(ExecutionUnit,執行單元).

ICU從指令高速緩存中讀取指令,指令高速緩存是一個特殊的高速緩存存儲器,它包含最近訪問的指令;

指令解碼邏輯接收實際的程序指令,并將它們轉換成一組根本的操作;

加載和存儲單元通過數據高速緩存訪問存儲器,這是一個高速存儲器,包含最近訪問的數據值;

退役單元紀錄正在進行的處理,并確保遵守機器級程序的順序語意.退役單元控制著存放器的更新;

任何對程序狀態的更新都只會在指令退役時才發生,只有在處理器能夠確信這條指令的所有分支都預測

正確了,才能這樣做;

執行時間的范圍從根本整數操作的一個周期,到加載,存儲,整數乘法和更常見的浮點操作的幾個周期,到

除法和其他復雜操作的許多個周期;

處理器的幾個功能單元被流水線化了,這意味著在前一個操作完成之前,它們就可以開始一個新的操作;

浮點乘法器要求連續的操作之間至少要?兩個周期,而兩個除法器根本就沒有流水線化;

標記可以與并不會寫道存放器文件中的中間值相關聯;

循環展開本身只會幫助整數求利情況中代碼的性能,因為我們的其他情況是被功能單元的執行時間限制

的;

編譯器可以很容易的執行循環展開,只要優化級別設置得足夠高(例如,優化選項-02)許多編譯器都能例

行公事的做到這一點,在命令行上以'-funroll-loops'調用GCC,它會執行循環展開;

有時候,我們能夠通過使用指針,而不是數組改良一個程序的性能;編譯器對數組代碼應用非常高級的優

化,而對指針只應用最小限度的優化,為了可讀性的緣故,通常數組代碼更可取一些;

循環分割:我們可以通過將一組合并操作分割成兩個或更多的局部,并在最后合并結果來提高性能;

對于整數數據類型的情況,總共只有八個整數據傳區可用.其中兩個指向棧中的區域;

八個整數和八個浮點存放器的限制是IA32指令集的不幸產物,前面講到國的重命名消除了存放器名字和

存放器數據實際位置之間的聯系.在現代處理器中,存放器名字之簡單的用來標識在功能單元之間傳遞的

程序值.IA32只提供了很少量的這樣的標識符,限制了在程序中能表達的并行性的數量;

某個與及其相關的因素將浮點乘法能到達的CPE限制在『15而不是理論極限值的1.0;

程序優化的通用原那么對各種不同的機器都適用,即使某種特殊的特性組合導致最優性能依賴于特殊的

機器;

到目前,臺式機或效勞器中幾乎每個處理器都支持投機執行;

一個存儲器讀的結果依賴于一個非常近的存儲器的寫叫做讀寫相關,它導致了處理速度的下降;

movl%edx,(%ecx)被翻譯成兩個操作:storeaddr指令計算存儲操作的地址,創立一個存儲緩沖區中的條

目,并設置該條目的地址字段;storedata指令設置該條目的數據字段;

通常,處理器/存儲器接口是處理器設計中最復雜的局部之一.不查閱詳細的文檔和使用機器分析工具,我

們只能給出實際行為的一個假象的描述;

由于存儲器操作占到了程序很大一局部,存儲器子系統優化成以獨立的存儲器操作來提供更大的并行性;

優化程序性能的根本策略:

1.高級設計,為手邊的問題選擇適當的算法和數據結構;

2.根本編碼原那么,消除函數的連續調用,在可能時,將計算移到循環外.考慮有選擇的妥協程序的模塊性

以獲得更大的效率;消除不必要的存儲器引用,引入中間變量求保存中間結果,只有在最后的值計算由求

的時候,才將結果存放到數組或全局變量中;

3.低級優化.嘗試各種于數組代碼相對的指針形式;通過展開循環降低循環開銷;通過諸如迭代分割之類

的技術,找到使用流水線化的功能單元的方法;

Unix系統提供了一個剖析程序GPROF.這個程序產生兩種形式的信息.首先,它確定程序中每個函數花費

了多少CPU時間.其次,它計算每個函數被調用的次數,以調用函數來分析;

庫函數qsort是際遇快速排序算法進行排序的;

剖析是工具箱中一個很有用的工具,但是它不應該是唯一一個,即使測量不是很準確,特別是對較短的運

行時間來說.結果只適用于被測試的那些特殊的數據;

Amdahl定律的主要觀點:要想大幅度提高整個系統的速度,我們必須提高整個系統很大一局部的速度;

Amdahl定律描述了一個改良任何過程的通用原那么.除了適用于提高計算機系統的速度外花還能指導

一個公司試著降低生產剃須刀的本錢,或是指導一個學生改良他或她的平均績點.或許它在計箕機世界里

最有意義,在計算機世界中,我們通常將性能提高?倍或更多.只有通過優化系統很大?局部才能獲得這

么高的提高率;

第六章:存儲器層次結構

RAM分SRAM和DRAM

DDR-SDRAM:雙倍數據速率同步DRAM.通過時鐘的兩個邊沿作為控制信號,從而使DRAM的速度加倍.

ROM是以它們能被重編程的次數和對它們進行重新編程的機制來區分的:PROM,EPROM,EEDROM.閃

存是基于EEPROM的;

存儲在ROM設備中的程序通常被稱為固件,當一個計算機系統通電以后,它會運行存儲在ROM中的固件;

總線是一組并行的導線,能攜帶數據,地址和控制信號;

系統總線將CPU連接到I/O橋接器,存儲器總線將I/O橋接器連接到主存,1/0橋接器也將系統總線和存

儲器總線連接到I/O總線;

扇區包含相等數量的數據位,扇區之間由一些間隙分隔開,間隙用來存儲標識扇區的格式化位;

多個盤面時,任何時刻,所有讀寫頭都位于同一柱面上;

磁盤是以扇區大小的塊來讀寫數據的;

磁盤中有一個小的硬件/固件設備,稱為磁盤控制器,維護著邏輯塊號和實際磁盤扇區之間的映射關系;

在磁盤可以存儲數據之前,它必須被磁盤控制器格式化,這包括標識扇區的信息填寫扇區之間的間隙,標

識出外表有故障的柱面并且不適用它們,以及在每個區中預留出一組柱面作為備用;

在使用存儲器映射I/O的系統中,地址空間中有一塊地址是為與I/O設備通信保存的,每個這樣的地址稱

為一個I/O端口;

現代計算機頻繁使用基于SRAM的高速緩存;

有良好局部性的程序比局部性差的程序運行得更快;

代碼區別于程序數據的一個重要屬性是在運行時不能修改;

局部變量的反更引用是好的,步長為1的引用模式是好的;

如果你的程序需要的數據是存儲在CPU存放器中的,那么在執行期間,在零個周期內就能訪問到它們;如

果存儲在高速緩存中,需要1-10個周期;如果存儲在主存中,需要50-100個周期;而如果存儲在磁盤上,需

要大約20OOOOOO個周期;

具有良好局部性的程序傾向于一次又一次的訪問相同的數據項集合,或是傾向于訪問鄰近的數據項集合;

恃別的,我們將注意力集中在CPU和主存之間作為緩存區域的高速緩存存儲器上,因為它們對應用程序

性能影響最大;

隨機訪問存儲器(random-accessmemory,RAM)分為兩類一靜態的和動態的.靜態RAM(SRAM)比動態

RAM(DRAM)更快,但也貴得多;

SRAM將每個位存儲在一個雙穩態的存儲器單元里;每個單元是用一個六晶體管電路來實現的.這個電路

有這樣一個屬性,它可以無限期的保持在兩個不同的電壓配置或狀態之一;

DRAM存儲器可以制造的非常密集一每個單元由一個電容和一個訪問晶體管組成;

只要有電,SRAM就是持續的,與DRAM不同,它不需要刷新.SRAM的存取比DRAM快.SRAM對諸如光

和電噪音這樣的干擾不敏感.代價是SRAM單元比DRAM單元使用更多的晶體管,因而沒那么密集,而且

更貴,功耗更大;

電路設計者將DRAM組織成二維陣列而不是線性數組的一個原因是降低芯片上地址管腳的數量;

DRAM芯片包裝在存儲器模塊中,它插到主板的擴展槽上;二維陣列組織的缺點是必須分兩步發送地址,

這增加了訪問時間;

增強的DRAM:FPMDRAM(fastpagemodeDRAM,快頁模式DRAM);EDODRAM(extendeddataout

DRAM,擴展數據輸出DRAM);SDRAM(synchronousDRAM,同步DRAM);DDRSDRAM(double

data-ratesynchronousDRAM,雙倍數據速率同步);RambusDRAM;

一些系統在固件中提供了少量的輸入和輸出函數一例如,PC的BIOS例程;I/O橋接都將系統總線的電信

號翻譯成存儲器總線的電信號;

磁盤是由一個或多個疊放在一起的盤片組成的,它們放在一個密封的包裝里,正個裝置通常別叫做磁盤驅

動滯,雖然我們通常簡稱位磁盤;

對扇區的訪問時間有三個主要局部:尋道時間,旋轉時間,傳送時間;

從存儲器中讀一個512字節扇區大小的塊的時間對SRAM來說大約是256ns,對DRAM來說大約是

4000ns.磁盤訪問時間比SRAM大約大4000倍;比DRAM大約大2500倍;

當操作系統想要執行一個I/O操作時,例如讀一個磁盤扇區的數據到主存,操作系統會發送一個命令到磁

盤控制器,讓它讀某個邏輯塊號.控制器上的固件執行一個快速表查找,將一個邏輯塊號翻譯成一個(盤面,

磁道,扇區)的三元組,這個三元組唯一的標識了對應的物理扇區,控制器上的硬件解釋這個三元組,將I/O

頭移動到適當的柱面,等待扇區移動到I/O頭下,將I/O頭感知到的位放到控制器上的一個小緩沖區中,

然后將它們拷貝到主存中;

像圖形卡,監視器,鼠標,鍵盤,這樣的設備都是通過諸如Intel的PCI(peripheralcomponentinterconnect

外圍設備互聯)總線這樣的I/O總線連接到CPU和主存的;

雖然I/O總線比系統總線和存儲器總線慢,但是它快頁容納種類繁多的第三方I/O設備;

USB控制器是一個將設備連接到USB的電路,USB的吞吐率可以到達12Mbit/s,是為慢速或中速串行設

備設計的;

圖形卡包含硬件和軟件邏輯,它們負責代表CPU在顯示器上畫像素;

磁盤控制器包含硬件和軟件邏輯,它們用來代表CPU讀寫磁盤;

當一個設備連接到總線時,它與一個或多個端口相關聯;

邏輯塊的概念不僅能夠提供應操作系統一個更簡單的接口,還能夠提供一層抽象,使得磁盤更加健壯;

磁盤中有一個小的硬件/固件設備,稱為磁盤控制器,維護著邏輯塊號和實際磁盤扇區之間的映射關系;

操作系統用主存來緩存磁盤文件系統中最近被使用的磁盤塊;

對于取指令來說,循環有好的時間和空間局部性,循環體越小,循環次數越多,局部性越好;

一般而言,層次結構中較低層的設備的訪問時間較長,因此為了補償這些較長的訪問時間,傾向于使用較

大的塊;

在一個有虛擬存儲器的系統中,DRAM主存作為存儲在磁盤上的數據塊的緩存,是有操作系統軟件和

CPU上的地址翻譯硬件共同管理的;

高速緩存確定一個請求是否命中燃后抽取出被請求的字的過程,分為三步:組選擇;行匹配;字拍??;

沖突不命中在真實的程序中很常見,會導致令人困惑的性能問題;

即使程序有良好的空間局部性,而我們的高速緩存中也有足夠的空間來存放兩個數組的塊,每次引用還是

會導致沖突不命中,這是因為這些塊被映射到了同一個高速緩存組;

直寫高速緩存通常是非寫分配的,寫回高速緩存通常是寫分配的;

只保存指令的高速緩存稱為i-cache,只保存程序數據的高速緩存稱為d-cache.一個典型的桌面系統CPU

芯片本身就包括?個Lii-cache和?個Lid-cache;

一方面,較大的高速緩存可能會提高緩存命中率;另一方面,使得大存儲器運行得更快總是要難一些的;

現代系統通常會折中,使高速緩存塊包含4-8個字;

傳統上,努力爭取時鐘頻率的高性能系統會選擇直接映射L1高速緩存,而在較低層上使用比擬小的相關

度,但是沒有固定的規那么;

一般而言,高速緩存越往下層,越可能使用寫回而不是直寫;

因為一行總是存儲一個塊,術語行和塊通常互換使用,例如,系統專家總是說高速緩存的行人小,實際上他

們指的是塊大??;

理解存儲器層次結構本質的程序員能夠利用這些知識,編寫出更有效的程序,無論具體的存儲系統是怎樣

的.特別的,我們推薦以下技術:將你的注意力集中在內部循環上,大局部計算和存儲器訪問都發生在這里;

通過按照數據對象存儲在存儲器中的順序來讀數據,從而使得你程序中空間局部性最大;一旦從存儲器中

讀入一個數據對象,就盡可能多的使用它,從而使得你程序中的時間局部性最大;記住,不命中率只是確定

你代碼性能的一個因素,存儲器訪問數量也扮演著重要的角色,有時候要在兩者之間做一下折中;〃(6.6未

看)

第七章:連接

連接器完成的兩個主要任務:符號解析和重定位.

編譯器和匯編器生成地址。開始的代碼和數據節.

目標文件:可重定位目標文件,可執行目標文件,共享目標文件.

ELF可重定位目標文件:ELF頭以一個16字節的序列開始,這個序列描述了字的大小和生成該文件的系

統的字節順序..test.:已編譯程序的機器代碼;.rodata.:只讀數據;.data.:已初始化的全局變量;.bss.:為初始

化的全局變量;.symtab.:存放程序中被定義和引用的函數和全局變量的信息.

連接器上下文中有三種符號:塊內定義的全局符號;塊外定義的全局符號和本地符號;

定義為帶cstatic屬性的本地過程變量不是在棧中管理的,編譯器在.data.和.bss.中為每個定義分配空間;

任何聲明為static屬性的全局變量或函數是模塊私有的;

編譯相來保證本地符號的唯一性;

當編譯器遇到一個不是在當前模塊中定義的變量時,它會假設該符號是在其他某個模塊中定義的,生成一

個連接器符號表表目;

運行時的存儲器映像:未使用。只讀段->讀寫段->運行時堆->共享庫的存儲器映射區域。用戶棧。內核

虛擬存儲器;

加載運行:可執行文件中段頭表的指導下,加載代碼和數據->程序入口點_start->初始化函數->注冊函數

?>主函數?>返回;

c的啟動代碼對于每個c程序都是相同的,都需要跳到main函數;

鏈接就是將不同局部的代碼和數據收集和組合成為一個單一文件的過程,這個文件可被加載到存儲器并

執行.鏈接可以執行于編譯時,也就是在源代碼被翻譯成機器代碼時丁也可以執行于加載時,也就是在程序

被加我器加載到存儲器并執行時;甚至執行于運行時,由應用程序來執行;

鏈接器在軟件開發中扮演著一個關鍵的角色,因為他們使得別離編譯成為了可能;

理解鏈接器將幫助你構造大型程序;理解鏈接器將幫助你防止一些危險的編程錯誤;理解鏈接器將幫助你

理解語言的作用域規那么是如何實現的;理解鏈接器將幫助你理解其他重要的系統概念;理解銃接器使你

能夠開發共享庫;

目標文件純粹是字節塊的集合,這些塊中,有些包含程序代碼,有些那么包含程序數據,而其他的那么包含

指導鏈接器和加載器的數據結構.鏈接器將這些塊連接起來,確定鏈接塊的運行時位置,并且修改代碼和

數據塊中的各種位置.鏈接器對目標機器了解甚少,產生目標文件的編譯器和匯編器已經完成了大局部工

作;

目標文件有三種形式:1.可重定位目標文件包含二進制代碼和數據,其形式可以在編譯時與其他可重定位

日標文件合并起來,創立一個可執行目標文件;2.可執行目標文件包含二進制代碼和數據,其形式可以被直

接拷貝到存儲器并執行;3.共享巨標文件一種特殊類型的可重定位目標文件,可以在加載活著運行時,被動

態的加載到存儲器并鏈接;

各個系統之間,目標文件的格式都不相同;

SUNSolaris使用的是UnixELF(可執行可鏈接格式).盡管我們的討論集中在ELF上,但是不管是那種格

式,根本的概念是相似的;

ELF頭以一個16字節的序列開始,這個序列描述了字的大小和生成該文件的系統的字節順序.ELF頭剩

卜.的局部包含幫助徒接器解析用解釋目標文件的信息.其中包括了ELF頭的大小,目標文件的類型(可重

定位,可執行或是共享等),機器類型(IA32等),節頭部表的文件偏移,以及節頭部表中的表目大小和數量.不

同節的位置和大小是有節頭部表描述的,其中目標文件中每個節都有一個固定大小的表目;

每個可重定位目標文件m都有一個符號表,它包含m所定義和引用的符號的信息.在鏈接器的上下文中,

有二種不同的符號.由m定義并能被其他模塊引用的全局符號.全局鏈接器符號對應于非靜態的c函數

以及被定義為不帶c的static屬性的全局變量;2.由其他模塊定義的并被模塊m引用的全局符號.這些符

號稱為外部符號,對應于定義在其他模塊中的c函數和變量;3.只被模塊m定義和引用的本地符號.有的

本地鏈接器符號對應于代static屬

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論