參考書籍:《深入理解計算機系統》
基本概念
ISA
指令集架構(Instruction Set Architecture):定義機器級程式的格式和行為,它定義了處理器狀態、指令的格式,以及每條指令對狀態的影響,
結構(struct)
C語言的struct宣告創建一個資料型別,可將不同型別的物件聚合到一個物件中,用名字來參考結構的各個組成部分,類似陣列的實作,結構的所有組成部分都存放在記憶體中一段連續的區域內,而指向結構的指標就是結構第一個位元組的地址,
聯合(union)
聯合允許以多種型別來參考一個物件,聯合宣告的語法與結構一樣,但聯合是用不同的欄位來參考相同的記憶體塊,故修改一個成員會影響其他所有成員,
對齊
許多計算機系統對基本資料型別的合法地址做出了一些限制,要求某種型別物件的地址必須是某個值K(通常是2、4或8)的倍數,無論資料是否對齊,x86-64硬體都能正確作業,不過,Intel還是建議要對齊資料以提高記憶體系統的性能(避免一個double型別的資料存放在兩個8位元組記憶體塊導致需要執行兩次記憶體訪問才能訪問到的情況),對齊原則是任何K位元組的基本物件的地址必須是K的倍數,
強制對齊
如果資料沒有對齊,某些型號的Intel和AMD處理器對于有些實作多媒體操作的SSE指令,就無法正確執行,這些指令對16位元組資料塊進行操作,在SSE單元和記憶體之間傳送資料的指令要求記憶體地址必須是16的倍數,任何試圖以不滿足對齊要求的地址來訪問記憶體都會導致例外,
緩沖區溢位
計算機對接收的輸入資料沒有進行有效的檢測,向緩沖區內填充資料時超過了緩沖區本身的容量,而導致資料溢位到被分配空間之外的記憶體空間,使得溢位的資料覆寫了其他記憶體空間的資料,
MMX、SSE與AVX
SIMD(單指令多資料流)是一種采用一個控制器來控制多個處理器,同時對一組資料中的每一個分別執行相同的操作從而實作空間上的并行性的技術,在微處理器中,單指令多資料流技術則是一個控制器控制多個平行的處理微元,如MMX、SSE和AVX,
MMX是由Intel開發的一種SIMD多媒體指令集,共有57條指令,它集成在奔騰MMX處理器上,以提高其多媒體資料的處理能力,
SSE指令集是繼MMX的擴充指令集,它提供了70條新指令,
AVX指令集,在單指令多資料流計算性能增強的同時也沿用了MMX/SSE指令集,不過和MMX/SSE的不同點在于增強的AVX指令,從指令格式上就發生了很大的變化,
關鍵點
摩爾定律
在集成電路上所容納的晶體管數目,約每隔18個月便會增加一倍,性能也將提升一倍,
x64虛擬地址范圍
x64CPU僅支持64位虛擬地址中的48位,這48位虛擬地址被運行在該CPU上的軟體使用,對于用戶模式地址,64位虛擬地址中的高16位總是被設定為0x0000;對于內核模式地址,總是設定為0xFFFF,
資料格式
由于是從16位體系結構擴展成32位的,Intel用術語“字(Word)”表示16位資料型別,因此,稱32位數為“雙字(double words)”,稱64位數為“四字(quad words)”,下圖給出了C語言基本資料型別對應的x86-64表示,

整數暫存器
一個x86-64的CPU包含一組16個存盤64位值的通用目的暫存器,這些暫存器用來存盤整數資料和指標,

運算元格式
各種不同的運算元的可能性分為三種型別——立即數、暫存器和記憶體參考,
- 立即數偏移:Imm
- 基址暫存器:base register
- 變址暫存器:index register
- 比例因子:s,必須是1、2、4或者8

條件碼
除了整數暫存器,CPU還維護著一組單個位的條件碼暫存器,它們描述了最近的算識訓邏輯操作的屬性,可以檢測這些暫存器來執行分支指令,
- CF:進位標志,最近的操作使最高位產生了進位;
- ZF:零標志,最近的操作得出的結果為0;
- SF:符號標志,最近的操作得到的結果為負數;
- OF:溢位標志,最近的操作導致一個補碼溢位,
除了leap(加載有效地址)之外,其他的算術和邏輯運算都會設定條件碼,
運行時堆疊
如圖是運行時堆疊的通用結構,包括把它劃為堆疊幀,
當前正在執行的程序的幀總是在堆疊頂,當程序P呼叫程序Q時,會把回傳地址壓入堆疊中,指明當Q回傳時,要從P程式的哪個位置繼續執行,我們把這個回傳地址當做P的堆疊幀的一部分,因為它存放的是與P相關的狀態,
Q的代碼會擴展當前堆疊的邊界,分配它的堆疊幀所需的空間,在這個空間中,它可以保存暫存器的值,分配區域變數空間,為它呼叫的程序設定引數,

轉移控制
如圖所示,call指令將控制轉移到一個函式的起始,而ret指令回傳到這次呼叫后面的那條指令,
- %rip是程式計數器的值,%rsp是堆疊指標的值,
- 0x400563是一條
call指令,0x400568是緊跟其后,0x400540是被跳轉到的函式的第一條指令,

堆疊上的區域存盤
有時候,區域變數必須存放在記憶體中,比如:
- 暫存器不足夠存放所有的本地資料;
- 對一個區域變數使用地址運算子‘&’,因此必須能夠為它產生一個地址;
- 某些區域變數是陣列或結構,因此必須能夠通過陣列或結構參考被訪問到,
一般來說,程序通過減小堆疊指標在堆疊上分配空間,分配的結果作為堆疊幀的一部分,標號為“區域變數”,
被呼叫者保存暫存器和呼叫者保存暫存器
被呼叫者保存暫存器:當程序P呼叫程序Q時,Q必須保存這些暫存器的值,保證它們的值在Q回傳到P時與Q被呼叫時是一樣的,程序Q保存一個暫存器的值不變,要么就是根本不去改變它,要么就是把原始值壓入堆疊中,改變暫存器的值,然后在回傳前從堆疊中彈出舊值,
呼叫者保存暫存器:任何函式都能修改它們,程序P在某個此類暫存器中有區域資料,然后呼叫程序Q,因為Q可以隨意修改這個暫存器,所以P在呼叫之前首先保存好這個資料,
指令使用
資料傳送
MOV類指令把資料從源位置復制到目的位置,不做任何變化,
movb、movw、movl和movq指令都執行同樣的操作,主要區別在于它們操作的資料大小不同:分別是1、2、4和8位元組,

整數算術和邏輯操作
leaq指令實際上是movq指令的變形,它的第一個運算元S看上去是一個記憶體參考,但該指令并不是從指定的位置讀入資料,而是將有效地址寫入到目的運算元(必須是暫存器),這條指令可以為后面的記憶體參考產生指標,還可以簡潔地描述普通的算術操作,與有效地址計算無關,
一元操作只有一個運算元,既是源又是目的,既可以是一個暫存器,又可以是一個記憶體位置,
二元操作中,第一個運算元可以是立即數、暫存器或是記憶體位置,第二個操作可以是暫存器或是記憶體位置,注意,當第二個運算元為記憶體地址時,處理器必須從記憶體中讀出值,執行操作,再把結果寫回記憶體,
移位操作先給出移位量,再給出要移位的數,移位量可以是一個立即數,或者放在單位元組暫存器%cl中(由%cl的低位決定,具體位數與操作的資料大小有關),

特殊的算術操作
對于乘法指令imulq S,D,這種“雙運算元”的乘法指令從兩個64位運算元產生一個64位乘積(只截取了低64位),而下面的“單運算元”乘法指令imulq S,計算了兩個64位值的全128位乘積,
為了實作“單運算元”,這種乘法指令要求一個引數必須在暫存器%rax中,另一個作為指令的源運算元給出,然后將乘積存放在暫存器%rdx(高64位)和%rax(低64位)中,

CMP和TEST指令
CMP和TEST指令只設定條件碼而不改變任何其他暫存器,對于其他的行為,CMP和SUB一樣,TEST和AND一樣,
注意,雖然指令comq b,a列出的順序先是b再是a,但實際上比較的是a和b,

跳轉指令
jmp指令是無條件跳轉,它可以是直接跳轉(跳轉目標是作為指令的一部分編碼的),也可以是間接跳轉(跳轉目標是從暫存器或記憶體位置中讀出的),
而有條件跳轉只能是直接跳轉,它們根據條件碼的某種組合選擇是否進行跳轉,

條件傳送指令
當傳送條件滿足時,指令就把源值S復制到目的R,

條件陳述句
利用條件控制來實作條件分支
C語言中的if-else陳述句的通用形式為:
if (test-expr)
then-statement
else
else-statement
對于這種通用形式,匯編實作通常會使用下面這種形式,這里,我們用C語法來描述控制流:
t = test-espr;
if(!t)
goto false;
then-statement
goto done;
false:
else-statement
done:
利用條件傳送來實作條件分支
考慮下面的條件運算式和賦值的通用形式:
v = test-expr ? then-expr : else-expr;
用條件控制轉移的標準方法來編譯這個運算式會得到如下形式:
if(!test-espr)
goto false;
v = then-expr
goto done;
false:
v = else-expr
done:
這段代碼包含兩個代碼序列:一個是對then-expr求值,另一個對else-expr求值,但基于條件傳送的代碼,會對then-expr和else-expr都求值,最終值的選擇基于對test-expr的求值,可以用下面的抽象代碼描述:
v = then-expr;
ve = else-expr;
t = test-expr;
if (!t)
v = ve
回圈陳述句
do-while回圈
loop:
body-statement
t = test-expr;
if (t)
goto loop;
while回圈
goto test;
loop:
body-statement
test:
t = test-expr;
if (t)
goto loop;
for回圈
GCC為for回圈產生的代碼是while回圈的兩種翻譯之一,這取決于優化的等級,也就是,跳轉到中間策略會得到如下goto代碼:
init-expr;
goto test;
loop:
body-statement
update-expr;
test:
t = test-expr;
if (t)
goto loop;
而guarded-do策略得到:
init-expr;
t = test-expr;
if (!t)
goto done;
loop:
body-statement
update-expr;
t = test-expr;
if (t)
goto loop;
done:
switch陳述句
switch陳述句可以根據一個整數索引值進行多重分支,它通過使用跳轉表這種資料結構使得實作更加高效,和使用一組很長的if-else陳述句相比,使用跳轉表的優點是執行開關陳述句的時間與開關情況的數量無關,
如圖所示,原始的C代碼有針對值100、102-104和106的情況,但是開關變數n可以是任意整數,編譯器首先將n減去100,把取值范圍移到0和6之間(補碼表示的負數會映射成無符號的大正數),創建一個新的程式變數——index,根據index的值,有五個不同的跳轉位置loc_A、loc_B、loc_C、loc_D和loc_def(默認目的地址),
執行switch陳述句的關鍵步驟是通過跳轉表來訪問代碼位置,在圖b代碼中的16行,一條goto陳述句參考了跳轉表,
C代碼將跳轉表宣告為一個有7個元素的陣列,每個元素都是一個指向代碼位置的指標,這些元素跨越0~6,對應于n的值100~106,

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/286386.html
標籤:其他
上一篇:JVM概述
下一篇:計算機網路之二:物理層
