導航:首頁 > 源碼編譯 > 編譯器管理存儲的寄存器

編譯器管理存儲的寄存器

發布時間:2023-03-26 17:42:09

1. 誰能給我講一下C語言中程序以及各類型數據存儲位置

我想很多人也是糊塗,以下文章寫得很好,故全文轉來,慢慢體會。

程序的內存分配(堆和棧區別)

一、預備知識 程序的內存分配

一個由c/C++編譯的程序佔用的內存分為以下幾個部分
1、棧區(stack) 由編譯器自動分配釋放 ,存放函數的參數值,局部變數的值等。其操作方式類似於數據結構中的棧。
2、堆區(heap) 一般由程序員分配釋放, 若程序員不釋放,程序結束時可能由OS回收 。注意它與數據結構中的堆是兩回事,分配方式倒是類似於鏈表,呵呵。
3、全局區(靜態區)(static),全局變數和靜態變數的存儲是放在一塊的,初始化的全局變數和靜態變數在一塊區域, 未初始化的全局變數和未初始化的靜態變數在相鄰的另一塊區域。 - 程序結束後有系統釋放
4、文字常量區 ?常量字元串就是放在這里的。 程序結束後由系統釋放
5、程序代碼區?存放函數體的二進制代碼。
二、例子程序
這是一個前輩寫的,非常詳細
//main.cpp
int a = 0; 全局初始化區
char *p1; 全局未初始化區
main()
{
int b; 棧
char s[] = "abc"; 棧
char *p2; 棧
char *p3 = "123456"; 123456\0在常量區,p3在棧上。
static int c =0; 全局(靜態)初始化區
p1 = (char *)malloc(10);
p2 = (char *)malloc(20);
分配得來得10和20位元組的區域就在堆區。
strcpy(p1, "123456"); 123456\0放在常量區,編譯器可能會將它與p3所指向的"123456"優化成一個地方。
}

二、堆和棧的理論知識

2.1申請方式
stack:
由系統自動分配。 例如,聲明在函數中一個局部變數 int b; 系統自動在棧中為b開辟空間
heap:
需要程序員自己申請,並指明大小,在c中malloc函數
如p1 = (char *)malloc(10);
在C++中用new運算符
如p2 = (char *)malloc(10);
但是注意p1、p2本身是在棧中的。

2.2
申請後系統的響應
棧:只要棧的剩餘空間大於所申請空間,系統將為程序提供內存,否則將報異常提示棧溢出。
堆:首先應該知道操作系統有一個記錄空閑內存地址的鏈表,當系統收到程序的申請時,
會遍歷該鏈表,尋找第一個空間大於所申請空間的堆結點,然後將該結點從空閑結點鏈表中刪除,並將該結點的空間分配給程序,另外,對於大多數系統,會在這塊內存空間中的首地址處記錄本次分配的大小,這樣,代碼中的delete語句才能正確的釋放本內存空間。另外,由於找到的堆結點的大小不一定正好等於申請的大小,系統會自動的將多餘的那部分重新放入空閑鏈表中。

2.3申請大小的限制
棧:在Windows下,棧是向低地址擴展的數據結構,是一塊連續的內存的區域。這句話的意思是棧頂的地址和棧的最大容量是系統預先規定好的,在WINDOWS下,棧的大小是2M(也有的說是1M,總之是一個編譯時就確定的常數),如果申請的空間超過棧的剩餘空間時,將提示overflow。因此,能從棧獲得的空間較小。
堆:堆是向高地址擴展的數據結構,是不連續的內存區域。這是由於系統是用鏈表來存儲的空閑內存地址的,自然是不連續的,而鏈表的遍歷方向是由低地址向高地址。堆的大小受限於計算機系統中有效的虛擬內存。由此可見,堆獲得的空間比較靈活,也比較大。

2.4申請效率的比較:
棧由系統自動分配,速度較快。但程序員是無法控制的。
堆是由new分配的內存,一般速度比較慢,而且容易產生內存碎片,不過用起來最方便.
另外,在WINDOWS下,最好的方式是用VirtualAlloc分配內存,他不是在堆,也不是在棧是直接在進程的地址空間中保留一快內存,雖然用起來最不方便。但是速度快,也最靈活。

2.5堆和棧中的存儲內容
棧: 在函數調用時,第一個進棧的是主函數中後的下一條指令(函數調用語句的下一條可執行語句)的地址,然後是函數的各個參數,在大多數的C編譯器中,參數是由右往左入棧的,然後是函數中的局部變數。注意靜態變數是不入棧的。
當本次函數調用結束後,局部變數先出棧,然後是參數,最後棧頂指針指向最開始存的地址,也就是主函數中的下一條指令,程序由該點繼續運行。
堆:一般是在堆的頭部用一個位元組存放堆的大小。堆中的具體內容有程序員安排。

2.6存取效率的比較

char s1[] = "aaaaaaaaaaaaaaa";
char *s2 = "bbbbbbbbbbbbbbbbb";
aaaaaaaaaaa是在運行時刻賦值的;
而bbbbbbbbbbb是在編譯時就確定的;
但是,在以後的存取中,在棧上的數組比指針所指向的字元串(例如堆)快。
比如:
#include
void main()
{
char a = 1;
char c[] = "1234567890";
char *p ="1234567890";
a = c[1];
a = p[1];
return;
}
對應的匯編代碼
10: a = c[1];
00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh]
0040106A 88 4D FC mov byte ptr [ebp-4],cl
11: a = p[1];
0040106D 8B 55 EC mov edx,dword ptr [ebp-14h]
00401070 8A 42 01 mov al,byte ptr [edx+1]
00401073 88 45 FC mov byte ptr [ebp-4],al
第一種在讀取時直接就把字元串中的元素讀到寄存器cl中,而第二種則要先把指針值讀到edx中,在根據edx讀取字元,顯然慢了。

2.7小結:
堆和棧的區別可以用如下的比喻來看出:
使用棧就象我們去飯館里吃飯,只管點菜(發出申請)、付錢、和吃(使用),吃飽了就走,不必理會切菜、洗菜等准備工作和洗碗、刷鍋等掃尾工作,他的好處是快捷,但是自由度小。
使用堆就象是自己動手做喜歡吃的菜餚,比較麻煩,但是比較符合自己的口味,而且自由度大

一般認為在c中分為這幾個存儲區

1棧 - 有編譯器自動分配釋放
2堆 - 一般由程序員分配釋放,若程序員不釋放,程序結束時可能由OS回收
3全局區(靜態區),全局變數和靜態變數的存儲是放在一塊的,初始化的全局變數和靜態變數在一塊區域,未初始化的全局變數和未初始化的靜態變數在相鄰的另一塊區域。- 程序結束釋放
4另外還有一個專門放常量的地方。 - 程序結束釋放
在函數體中定義的變數通常是在棧上,用malloc, calloc, realloc等分配內存的函數分配得到的就是在堆上。在所有函數體外定義的是全局量,加了static修飾符後不管在哪裡都存放在全局區(靜態區),在所有函數體外定義的static變數表示在該文件中有效,不能extern到別的文件用,在函數體內定義的static表示只在該函數體內有效。另外,函數中的"adgfdf"這樣的字元串存放在常量區。比如:

代碼:

int a = 0; //全局初始化區
char *p1; //全局未初始化區
main()
{

int b; //棧
char s[] = "abc"; //棧
char *p2; //棧
char *p3 = "123456"; //123456\0在常量區,p3在棧上。
static int c = 0; //全局(靜態)初始化區
p1 = (char *)malloc(10);
p2 = (char *)malloc(20); //分配得來得10和20位元組的區域就在堆區。
strcpy(p1, "123456"); //123456\0放在常量區,編譯器可能會將它與p3所指向的"123456"優化成一塊。
}

還有就是函數調用時會在棧上有一系列的保留現場及傳遞參數的操作。棧的空間大小有限定,vc的預設是2M。棧不夠用的情況一般是程序中分配了大量數組和遞歸函數層次太深。有一點必須知道,當一個函數調用完返回後它會釋放該函數中所有的棧空間。棧是由編譯器自動管理的,不用你操心。
堆是動態分配內存的,並且你可以分配使用很大的內存。但是用不好會產生內存泄漏。並且頻繁地malloc和free會產生內存碎片(有點類似磁碟碎片),因為c分配動態內存時是尋找匹配的內存的。而用棧則不會產生碎片。

在棧上存取數據比通過指針在堆上存取數據快些。一般大家說的堆棧和棧是一樣的,就是棧(stack),而說堆時才是堆heap。棧是先入後出的,一般是由高地址向低地址生長。

2. 存儲器層次結構中的緩存

《深入理解計算機系統》p422

6.1 存儲器層次結構中的緩存

一般而言,高速緩存( cache ,讀作「 cash 」)是一個小而快速的存儲設備,它作為存儲在更大、也更慢的設備中的數據對象的緩沖區域。使用高速緩存的過程稱為緩存( caching ,讀作「 cashing 」)。存儲器層次結構的中心思想是,對於每個 k ,位於 k 層的更快更小的存儲設備作為位於 k 十1層的更大更慢的存儲設備的緩存。換句話說,層次結構中的每一層都緩存來自較低一層的數據對象。例如,本地磁碟作為通過網路從遠程磁碟取出的文件(例如 Web 頁面)的緩存,主存作為本地磁碟上數據的緩存,依此類推,直到最小的緩存—— CPU 寄存器組。圖6-22展示了存儲器層次結構中緩存的一般性概念。第 k 十1層的存儲器被劃分成連續的數據對象組塊( chunk ),稱為塊( block )。每個塊都有一個唯一的地址或名字,使之區別於其他的塊。塊可以是固定大小的(通常是這樣的),也可以是可變大小的(例如存儲在 Web 伺服器上的遠程 HTML 文件)。例如,圖6-22中第 k 十1層存儲器被劃分成16個大小固定的塊,編號為0~15。

類似地,第 k 層的存儲器被劃分成較少的塊的集合,每個塊的大小與 k 十1層的塊的大小一樣。在任何時刻,第 k 層的緩存包含第 k 十1層塊的一個子集的副本。例如,在圖6-22中,第 k 層的緩存有4個塊的空間,當前包含塊4、9、14和3的副本。

數據總是以塊大小為傳送單元( transfer unit )在第 k 層和第 k +1層之間來回復制的。雖然在層次結構中任何一對相鄰的層次之間塊大小是固定的,但是其他的層次對之間可以有不同的塊大小。例如,在圖6-21中,L1和 LO 之間的傳送通常使用的是1個字大小的塊。L2和L1之間(以及I3和I2之間、L4和I3之間)的傳送通常使用的是幾十個位元組的

塊。而L5和L4之間的傳送用的是大小為幾百或幾千位元組的塊。一般而言,層次結構中較低層(離 CPU 較遠)的設備的訪問時間較長,因此為了補償這些較長的訪問時間,傾向於使用較大的塊。

1. 緩存命中

當程序需要第 k 十1層的某個數據對象 d 時,它首先在當前存儲在第 k 層的一個塊中查找 d 。如果 d 剛好緩存在第 k 層中,那麼就是我們所說的緩存命中( cache hit )。該程序直接從第 k 層讀取 d ,根據存儲器層次結構的性質,這要比從第 k +1層讀取 d 更快。例如,一個有良好時間局部性的程序可以從塊14中讀出一個數據對象,得到一個對第 k 層的緩存命中。

2. 緩存不命中

另一方面,如果第 k 層中沒有緩存數據對象 d ,那麼就是我們所說的緩存不命中( cache miss )。當發生緩存不命中時,第 k 層的緩存從第 k 十1層緩存中取出包含 d 的那個塊,如果第 k 層的緩存已經滿了,可能就會覆蓋現存的一個塊。

覆蓋一個現存的塊的過程稱為替換( replacing )或驅逐( evicting )這個塊。被驅逐的這個塊有時也稱為犧牲塊( victim block )。決定該替換哪個塊是由緩存的替換策略( replace — ment policy )來控制的。例如,一個具有隨機替換策略的緩存會隨機選擇一個犧牲塊。一個具有最近最少被使用 LRU )替換策略的緩存會選擇那個最後被訪問的時間距現在最遠的塊。

在第 k 層緩存從第 k 十1層取出那個塊之後,程序就能像前面一樣從第 k 層讀出 d 了。例如,在圖6-22中,在第 k 層中讀塊12中的一個數據對象,會導致一個緩存不命中,因為塊12當前不在第 k 層緩存中。一旦把塊12從第 k 十1層復制到第 k 層之後,它就會保持在那裡,等待稍後的訪問。

3. 緩存不命中的種類

區分不同種類的緩存不命中有時候是很有幫助的。如果第 k 層的緩存是空的,那麼對

任何數據對象的訪問都會不命中。一個空的緩存有時被稱為冷緩存( cold cache ),此類不命中稱為強制性不命中( compulsory miss )或冷不命中( cold miss )。冷不命中很重要,因為它們通常是短暫的事件,不會在反復訪問存儲器使得緩存暖身( warmed up )之後的穩定狀態中出現。

只要發生了不命中,第 k 層的緩存就必須執行某個放置策略( placement policy ),確定把它從第 k 十1層中取出的塊放在哪裡。最靈活的替換策略是允許來自第 k +1層的任何塊放在第 k 層的任何塊中。對於存儲器層次結構中高層的緩存(靠近 CPU ),它們是用硬體來實現的,而且速度是最優的,這個策略實現起來通常很昂貴,因為隨機地放置塊,定位起來代價很高。

因此,硬體緩存通常使用的是更嚴格的放置策略,這個策略將第 k 十1層的某個塊限制放置在第 k 層塊的一個小的子集中(有時只是一個塊)。例如,在圖6-22中,我們可以確定第 k 十1層的塊 i 必須放置在第 k 層的塊( i mod 4)中。例如,第 k 十1層的塊0、4、8和12會映射到第 k 層的塊0;塊1、5、9和13會映射到塊1;依此類推。注意,圖6-22中的示例緩存使用的就是這個策略。

這種限制性的放置策略會引起一種不命中,稱為沖突不命中( conflict miss ),在這種情況中,緩存足夠大,能夠保存被引用的數據對象,但是因為這些對象會映射到同一個緩存塊,緩存會一直不命中。例如,在圖6-22中,如果程序請求塊0,然後塊8,然後塊0,然後塊8,依此類推,在第 k 層的緩存中,對這兩個塊的每次引用都會不命中,即使這個緩存總共可以容納4個塊。

程序通常是按照一系列階段(如循環)來運行的,每個階段訪問緩存塊的某個相對穩定不變的集合。例如,一個嵌套循環可能會反復地訪問同一個數組的元素。這個塊的集合稱為這個階段的工作集( working set )。當工作集的大小超過緩存的大小時,緩存會經歷容量不命中( capacity miss )。換句話說就是,緩存太小了,不能處理這個工作集。

4. 緩存管理

正如我們提到過的,存儲器層次結構的本質是,每一層存儲設備都是較低一層的緩存。在每一層上,某種形式的邏輯必須管理緩存。這里,我們的意思是指某個東西要將緩存劃分成塊,在不同的層之間傳送塊,判定是命中還是不命中,並處理它們。管理緩存的邏輯可以是硬體、軟體,或是兩者的結合。

例如,編譯器管理寄存器文件,緩存層次結構的最高層。它決定當發生不命中時何時發射載入,以及確定哪個寄存器來存放數據。L1、L2和L3層的緩存完全是由內置在緩存中的硬體邏輯來管理的。在一個有虛擬內存的系統中, DRAM 主存作為存儲在磁碟上的數據塊的緩存,是由操作系統軟體和 CPU 上的地址翻譯硬體共同管理的。對於一個具有像 AFS 這樣的分布式文件系統的機器來說,本地磁碟作為緩存,它是由運行在本地機器上的 AFS 客戶端進程管理的。在大多數時候,緩存都是自動運行的,不需要程序採取特殊的或顯式的行動。

6.3.2 存儲器層次結構概念小結

概括來說,基於緩存的存儲器層次結構行之有效,是因為較慢的存儲設備比較快的存儲設備更便宜,還因為程序傾向於展示局部性:

1)利用時間局部性: 由於時間局部性,同一數據對象可能會被多次使用。一旦一個數據對象在第一次不命中時被復制到緩存中,我們就會期望後面對該目標有一系列的訪問命中。因為緩存比低一層的存儲設備更快,對後面的命中的服務會比最開始的不命中快很多。

2)利用空間局部性: 塊通常包含有多個數據對象。由於空間局部性,我們會期望後面對該塊中其他對象的訪問能夠補償不命中後復制該塊的花費。現代系統中到處都使用了緩存。正如從圖6-23中能夠看到的那樣, CPU 晶元、操作系統、分布式文件系統中和萬維網上都使用了緩存。各種各樣硬體和軟體的組合構成和管理著緩存。注意,圖6-23中有大量我們還未涉及的術語和縮寫。在此我們包括這些術語和縮寫是為了說明緩存是多麼的普遍。

3. C51編譯器支持的存儲器類型有哪些

c51存儲器類型有bit
sbit
data
xdata
bdata
pdata
sfr
code等,可能不全面有遺漏
對應的物理存儲器是:
bit,即位數據:數據存儲器位定址區,即20h~2fh的范圍,共16個位元組,16*8=128個位,位地址00h~7fh,連續的。
sbit:特殊功能寄存器中的位數據:只有能夠被8整除的那些特殊功能寄存器中的各個位才能被稱為sbit,位地址80h~ffh,不連續的,間斷的。
data:數據區,對51為00h~7fh共128個位元組,對52為00h~ffh,共256個位元組,用mov定址,前128用直接定址或寄存器(r0~r7)定址,後128用r0、r1間接定址。
xdata:外部數據區,0000h~ffffh連續,用dptr間接定址(movx指令)
bdata:位定址去的位元組,20h~2fh
sfr:特殊功能寄存器(80h~ffh),直接定址
pdata:外部數據區,p2口保持數據,用r0r1間接定址(movx指令)
code:程序存儲器,用movc指令只讀

4. java寄存器是什麼

這是速度最快的存儲場所,因為寄存器位於虛洞處理器內部,這一點和其他的存儲媒介都不一樣。不過寄存器個數是有限的。在內存中的寄存器區域是由編譯器根據需要來分配的。我們程序開發人員不能夠通過代碼來控制這個寄存器的分配。所以說,這第一個存儲區域寄存嘩運器,我們只能夠看看,而不能夠對其產生任何的影響。,也沒辦法在程序里頭感覺到寄存器的任亂譽梁何存在跡象。

5. CPU內部寄存器組結構及其功能是什麼

1.什麼是寄存器 所謂寄存器(register),它是CPU內部用來存放數據的一些小型存儲區域,用來暫時存放參與運算的數據襪沒和運算結果。其實寄存器就是一種常用的時序邏輯電路,但這種時序邏輯電路只包含存儲電路。寄存器的存儲電路是由鎖存器或觸發器構成的,因為一個鎖存器或觸發器能存儲1位二進制數,所以由N個鎖存器或觸發器可以構成N位寄存器。 2.寄存器與CPU指令 在講CPU的寄存器之前,我們先了解一下CPU指令系統。指令系統指的是一個CPU所能夠處理的全部指令的集合,Athlon XP和P4都是基於x86指令集,這是CPU的根本屬性,決定CPU運行什麼樣的程序。 指令一般分為:算術邏輯運算指令、浮點運算指令、位操作指令及其他的一些非運算指令,其中整數、地址、指令指針和浮點數據是按照數據形式來劃分的。通常我們把需要CPU進行不同處理的單個數據稱為標量數據(Scala Data)。標量數據既可以是整數數據,也可以是浮點數據。其中整數標量數據的存放區一般為通用寄存器(GPR),浮點標量數據的存放區一般為浮點寄存器(FPR)。與標量數據相對的是矢量數據(Vector Data),所謂矢量數據就是指一列需要由處理器作相同處理的數據集合。比如處理器在做MP3編碼的過程中,需要對內存中的音頻文件里的各位元組數據作相同的MP3編碼操作。那麼通常使用MMX或SSE這類單指令多數據流(SIMD)指令,將數個位元組打包為一組矢量數據,存放在MMX或SSE寄存器中,再送往相應的功能單元進行統一操作。 其中通用寄存器是處理器中最快的存儲器,用來保存參加運算的操作數和中間結果。在通用寄存器的設計上,RISC與CISC(也就是我們常說的x86架構)有著很大的不同。CISC的寄存器通常很少——只有8個通用寄存器。由於CPU在執行指令過程中,存在指令依賴性,在一定程度上使得x86 CPU不能在每個時鍾周期中立即發布大量的指令。所謂「依賴性」就是巧爛指令的執行需要前個指令的運算結果。比如程序員經常使用的分支程序,請看下面這個例子: A=C*1 B=A+2 只要變數A的值還不知道,B=A+2就不能進行運算。也就是說,只要指令1的結果沒有寫進寄存器,CPU調度器就不能把指令2發布到執行單元。由於程序分支會造成具有較長流水線CPU運行停滯的,目前常用的解決方法是採用分支預測。 不過,分支預測同樣存在一個問題:流水線越長,指令潛伏期也越長,等待前一指令運算結果的時間也越長,同樣會造成CPU運行停滯。我們知道,程序指令通常都有各類型的條件分支語句,通過驗證條件決定執行路線。但CPU執行單元內是通過一項特殊的預測機制選擇一條路線直接執行(這樣可以避免驗證語句條件而處於等待情況),然後在後面進行驗證。如果預測正確則繼續往下執行,如果發現以前的預測錯誤,那麼就必須返回原地重新開始,以前的指令就會作廢。 因此,管線越長告寬納,意味著出現分支預測錯誤的機會就越多,越多在管線內的指令會被清除掉,而且重新讓管道填滿指令的時間也會越長。對於普通處理器來說,如果出現分支預測錯誤,CPU就不得不將整條流水線清空後從錯誤的地方重新裝滿數據、重新執行。毫無疑問這將花更多的時間,整體性能就會下降。因此,針對通用寄存器少的問題,在x86架構中比較完美的解決方法就是增加寄存器的數量和採用「亂序執行」。3.為什麼寄存器不夠用 在上面我們已經提到,寄存器只是用來暫時存放指令值的,如果CPU需要把兩個值加起來,它需要用1個寄存器來存放運算結果,用2個寄存器來存放相加的數值。例如,在以下的方程式中:A = 2 + 4 * 在寄存器1儲存「2」; * 在寄存器2儲存「4」; * 在寄存器3儲存「寄存器1 + 寄存器 2」; 因為在微處理器裡面有超過3個寄存器,因此這個運算能夠輕易地執行,不會造成用光寄存器的情況。在這些運算被執行之後,所有的3個數值都能夠被保留並重新使用,因此如果我們再想在結果加上2的話,處理器只需要執行:寄存器 1 + 寄存器 3 就可以了。如果微處理器僅有2個剩餘的寄存器,而我們又需要再次使用2和4的值,那麼這些值在覆蓋結果A之前,必須儲存在主內存之中 。運算執行的過程則會變成如下所示: * 在寄存器1儲存 「2」; * 在寄存器2儲存「4」; * 在主內存的某個空間儲存「寄存器1 + 寄存器2」; 我們可以看到這里使用了其它的內存訪問過程,而在這期間其實還有我們沒有提到的其它處理過程,比如主內存的定位也需要佔據寄存器,以便讓CPU 告訴裝載/儲存單元該往哪裡發送數據 。如果我們需要使用到這些結果的話,那麼CPU將不得不首先到主內存中找回這些結果,把目前滿載的寄存器驅逐一些數據,把它們寫入主內存,然後再把尋找到的數據儲存在寄存器里。 這里大家應該能夠明白吧,對內存的訪問次數將會可怕地增加;你需要訪問內存的時間越多,那麼處理器等待工作完成的時間就越長——因而造成性能的下降。因此面對超標量CPU在並行處理大量運算,x86體系僅有的8個通用寄存器遠遠不能滿足需要,在同一時鍾周期中,如果有3個指令發布,你就需要3個輸出寄存器和6個輸入寄存器。我們該怎麼辦呢?聰明的工程師們發現了突破這個限制的方法:「寄存器重命名」。 4.寄存器重命名技術 寄存器重命名,是CPU在解碼過程中對寄存器進行重命名,解碼器把「其它」的寄存器名字變為「通用」的寄存器名字,本質上是通過一個表格把x86寄存器重新映射到其它寄存器,這樣可以讓實際使用到的寄存器遠大於8個。這樣做的好處除了便於前面指令發生意外或分支預測出錯時取消外,還避免了由於兩條指令寫同一個寄存器時的等待。 下面我們以一個超標量CPU執行8個算術指令為例:假設它在每個時鍾周期中能對2個指令解碼,引出計算結果是在指令發布後3個時鍾周期發生的: (1)在第1個時鍾周期,兩個指令發布:它們互不關聯,因此,它們將在3個時鍾周期後(第4個時鍾周期)引出; (2)在第2個時鍾周期,我們首次遇到了「指令依賴」,指令3需要指令2的結果,此時指令3不能開始發布; (3)如果是按序執行,指令4、5、6就不能在指令3前發布。只有在第5個時鍾周期時(指令2的結果已得到)才能發布指令3; (4)在第6個時鍾周期有個大問題:我們想把結果寫到寄存器R1,但這將改變指令5的結果。因此,我們只有在R1空閑時(第10個時鍾周期)才能發布指令6。 按照正常情況處理的話,盡管這個CPU每個時鍾周期可以對2個指令解碼,但它每個時鍾周期的指令執行數只有0.53。如果每次程序所需的寄存器正被使用,我們可以把數據放到其它的寄存器中,在第6個時鍾周期將寄存器R1重命名,指令6和指令8不再耽誤CPU的工作。結果是我們能夠將每個時鍾周期的指令執行數提高50%。寄存器重命名技術可以使x86 CPU的寄存器可以突破8個的限制,達到32個甚至更多。寄存器重命名技術現在已經深深地紮根於超標量CPU中了。5.亂序執行技術 除此之外,處理器工程師還引入了亂序執行技術,從一定程度上來緩解通用寄存器不足的問題。採用亂序執行技術的目的是為了使CPU內部電路滿負荷運轉並相應提高了CPU運行程序的速度。 這好比請A、B、C三個名人為春節聯歡晚會題寫橫幅「春節聯歡晚會」六個大字,每人各寫兩個字,如果這時在一張大紙上按順序由A寫好「春節」後再交給B寫「聯歡」,然後再由C寫「晚會」,那麼這樣在A寫的時候,B和C必須等待,而在B寫的時候C仍然要等待而A已經沒事了。但如果採用三個人分別用三張紙同時寫的做法,那麼B和C都不必等待就可以同時各寫各的了,甚至C和B還可以比A先寫好也沒關系(就像亂序執行),但當他們都寫完後就必須重新在橫幅上按「春節聯歡晚會」的順序排好(自然可以由別人做,就象CPU中亂序執行後的重新排列單元)才能掛出去。 不過,雖然採用寄存器重命名技術、亂序執行技術,但仍不能從根本上解決x86處理器通用寄存器不足的問題。以寄存器重命名技術來說,這種技術的寄存器操作相對於RISC來說,要花費一個時鍾周期來對寄存器進行重命名,這無形中降低了處理器性能以及流水線工作效率,也增加了程序和編譯器的優化難度。針對這個問題,最新的x86-64架構中(K8處理器),AMD在x86架構基礎上將通用寄存器和SIMD寄存器的數量增加了1倍:其中新增了8個通用寄存器以及8個SIMD寄存器作為原有x86處理器寄存器的擴充。 這些通用寄存器都工作在64位模式下,經過64位編碼的程序就可以使用到它們。這些64位寄存器稱為RAX、RBX、RCX、RDX、RDI、RSI、RBP、RSP、RIP以及EFLAGS,在32位環境下並不完全使用到這些寄存器,同時AMD也將原有的EAX等寄存器擴展至64位的RAX,這樣可以增強通用寄存器對位元組的操作能力。從擴充方式上看,EAX等寄存器可以看做是RAX的一個子集,系統仍然可以完整地執行以往的32位編碼程序。增加通用寄存器除了可高效存儲數據外,還可作為定址時的地址指針,從而縮短指令長度和指令執行時間,加快CPU的運算處理速度,同時也給編程帶來方便。 此外,為了保證K8的分支預測更有效率,K8的分支預測寄存器增加到64個。分支指令可以被設為真或假,而每個指令中的6位被分配到單獨一個預測寄存器中,只有預測寄存器被設定為「真」時,那些指向預測寄存器為「真」的指令結果才會被執行。其次由於所有的分支都能並行執行,CPU所花的時間同只執行單個分支的時間是相同的,降低了預測出錯的風險。第三由於CPU不再跳躍執行,它不會把程序代碼分成小塊。也就是說,稍前和稍後的程序代碼可以打包。這樣CPU能夠一並將它們發布,增大並行工作量。從而使性能提高10%~15%,特別是在整數代碼部分。 不過在x86-64中,寄存器的擴展部分似乎僅對於整數、地址數據有效。對浮點和向量數據則仍然保持原樣。我們能從K8向64位的擴展所獲得的好處,只不過是可以在同樣一條指令中,處理更大數值的整數數值以及管理空間更大的內存區域而已。而在32位的情況下,由於通用寄存器只能容納最大32位的數據,因此顯然要花費更多條指令對尺寸超過32位的數據進行處理。這種改進對伺服器、科學計算這樣的領域具有一定的意義,但顯然並不是普通家用環境急需的改進。 可以說,處理器的寄存器對處理器的性能有著巨大的影響。但是無論怎麼發展,通用型CPU目前還沒有脫離x86架構的限制,也許有一天,新的寄存器技術能讓我們的CPU變得更加功能強大!

6. 寄存器原理

寄存器原理:

寄存器應具有接收數據、存放數據和輸出數據的功能,它由觸發器和門電路組成。只有得到「存入脈沖」(又稱「存入指令」、「寫入指令」)時,寄存器才能接收數據;在得到「讀出」指令時,寄存器才將數據輸出。

寄存器存放數碼的方式有並行和串列兩種。並行方式是數碼從各對應位輸入端同時輸入到寄存器中;串列方式是數碼從一個輸入端逐位輸入到寄存器中。

寄存器讀出數碼的方式也有並行和串列兩種。在並行方式中,被讀出的數碼同時出現在各位的輸出端上;在串列方式中,被讀出的數碼在一個輸出端逐位出現。

(6)編譯器管理存儲的寄存器擴展閱讀:

寄存器最起碼具備以下4種功能。

①清除數碼:將寄存器里的原有數碼清除。

②接收數碼:在接收脈沖作用下,將外輸入數碼存入寄存器中。

③存儲數碼:在沒有新的寫入脈沖來之前,寄存器能保存原有數碼不變。

④輸出數碼:在輸出脈沖作用下,才通過電路輸出數碼。

僅具有以上功能的寄存器稱為數碼寄存器;有的寄存器還具有移位功能,稱為移位寄存器。

寄存器有串列和並行兩種數碼存取方式。將n位二進制數一次存入寄存器或從寄存器中讀出的方式稱為並行方式。將n位二進制數以每次1位,分成n次存入寄存器並從寄存器讀出,這種方式稱為串列方式。並行方式只需一個時鍾脈沖就可以完成數據操作,工作速度快,但需要n根輸入和輸出數據線。串列方式要使用幾個時鍾脈沖完成輸入或輸出操作,工作速度慢,但只需要一根輸入或輸出數據線,傳輸線少,適用於遠距離傳輸。

7. 通用寄存器有哪些

1、數據寄存器

數據寄存器主要用來保存操作數和運算結果等信息,從而節省讀取操作數所需佔用匯流排和訪問存儲器的時間。

2、變址寄存器

32位CPU有2個32位通用寄存器ESI和EDI。其低16位對應先前CPU中的SI和DI,對低16位數據的存取,不影響高16位的數據。

3、指針寄存器

32位CPU有2個32位通用寄存器EBP和ESP。其低16位對應先前CPU中的SBP和SP,對低16位數據的存取,不影響高16位的數據。

4、段寄存器

段寄存器是根據內存分段的管理虧襲模式而設置的。內存單元的物理地址由段寄存器的值和一個偏移量組合而成的,這樣可用兩個較少位數的值組合成跡鉛一個可訪問較大物理空間的內存地址。

5、指令指針寄存器

32位CPU把指令指針擴展到32位,並記作EIP,EIP的低16位與先前CPU中的IP作用相同。 指令指針EIP、IP(Instruction Pointer)是存放下次將要執行的指令在代碼段的偏移量。

(7)編譯器管理存儲的寄存器擴展閱讀

寄存器是CPU內部重要的數據存儲資源,用來暫存數據和地址,是匯編程序員能直接使用的硬體資源之一。由於寄存器的存取速度比內存快,所以,在用匯編語言編寫程序時,要盡可能充分利用寄存器的存儲功能。

寄存器一般用來保存程序的中間結果,為隨後的指令快速提供操作數,從而避免把中間結果存入內存,再讀取內存的操作。在高級語言(如:C/C++語言)中,也有定義變數為寄存器類型的,這就是提高寄存器利用率的一種可行的方法。

另外,由於寄存器的個數和容量都有限,不可能把所有中間結果都存儲在寄存器中,所以,要對寄存器進行適當的調度。根據指令的要求,如何安排適當的寄存器,避免操作數過多的傳送操作是一項細致而又周密的工作。

參考資料來源:網路-通用寄存器姿空好

8. 匯編語言寄存器都叫什麼

1、寄存器

32位寄存器有16個,分別是:

4個數據寄存器(EAX、EBX、ECX、EDX)。

2個變址和指針寄存器(ESI和EDI);2個指針寄存器(ESP和EBP)。

6個段寄存器(ES、CS、SS、DS、FS、GS)。

1個指令指針寄存器(EIP);1個標志寄存器(EFlags)。


2、數據寄存器

數據寄存器主要用來保存操作數和運算結果等信息,從而節省讀取操作數所需佔用匯流排和訪問存儲器猛基的時間。

32位CPU有4個32位通用寄存器:EAX、EBX、ECX和EDX。對低16位數據的取存,不會影響高16

位的數據,這些低16位寄存器分別命名為AX、BX、CX和DX,它和先前的CPU中的寄存器相一致。

4個16位寄存器又可分割成8個獨立的8位寄存器(AX:ah~al、BX:bh~bl、CX:ch~cl:DX:dh~dl)。

每個寄存器都有自己的名稱,可獨立存取。程序員可利用數據寄存器的這種「可合可分」的特性,靈活地處理字/

位元組的信息。

AX和al通常稱為累加器,用累加器進行的操作可能需要更少時間,累加器可用於乘、除、輸入/輸出等操作,

它們的使用頻率很高。

BX稱為基地址寄存器,它可作為存儲器指針來使用。

CX稱為計數寄存器,在循環和字元串操作時,要用它來控制循環次數;在位操作中,當移多位時,要用cl來

指明位移的位數。

DX稱為數據寄存器,在進行乘、除運算時,枝巧謹它可以為默認的操作數參與運算,也可用於存放I/O的埠地址。

在16位CPU中,AX、BX、CX和DX不能作為基址和變址寄存器來存放存儲單元的地址,但在32位CPU

中,寬核其32位寄存器EAX、EBX、ECX和EDX不僅可傳送數據、暫存數據、保存算術邏輯運算結果,而且也可

作為指針寄存器,所以,這些32位寄存器更具有通用性。


3、變址寄存器

32位CPU有2個32位通用寄存器ESI和EDI,其低16位對應先前CPU中的SI和DI,對低16位數據的

存取,不影響高16位的數據。

ESI、EDI、SI和DI稱為變址寄存器,它們主要用於存放存儲單元在段內的偏移量,用它們可實現多種存儲器

操作數的定址方式,為以不同的地址形式訪問存儲單元提供方便。

變址寄存器不可分割成8位寄存器,作為通用寄存器,也可存儲算術邏輯運算的操作數和運算結果。

它們可作一般的存儲器指針使用,在字元串操作指令的執行過程中,對它們有特定的要求,而且還具有特殊的

功能。


4、指針寄存器

32位CPU有2個32位通用寄存器EBP和ESP,其低16位對應先前CPU中的BP和SP,對低16位數

據的存取,不影響高16位的數據。

EBP、ESP、BP和SP稱為指針寄存器,主要用於存放堆棧內存儲單元的偏移量,用它們可實現多種存儲器

操作數的定址方式,為以不同的地址形式訪問存儲單元提供方便。

指針寄存器不可分割成8位寄存器,作為通用寄存器,也可存儲算術邏輯運算的操作數和運算結果。

它們主要用於訪問堆棧內的存儲單元,並且規定:

BP為基指針寄存器,用它可直接存取堆棧中的數據。

SP為堆棧指針寄存器,用它只可訪問棧頂。


5、段寄存器

段寄存器是根據內存分段的管理模式而設置的。內存單元的物理地址由段寄存器的值和一個偏移量組合而成

的,這樣可用兩個較少位數的值組合成一個可訪問較大物理空間的內存地址。

32位CPU有6個段寄存器,分別如下:

CS:代碼段寄存器 ES:附加段寄存器

DS:數據段寄存器 FS:附加段寄存器

SS:堆棧段寄存器 GS:附件段寄存器

在16位CPU系統中,只有4個段寄存器,所以,程序在任何時刻至多有4個正在使用的段可直接訪問,在

32位微機系統中,它有6個段寄存器,所以在此環境下開發的程序最多可同時訪問6個段。

32位CPU有兩個不同的工作方式:實方式和保護方式。在每種方式下,段寄存器的作用是不同的,有關規定

簡單描述如下:

實方式:段寄存器CS、DS、ES和SS與先前CPU中的所對應的段寄存器的含義完全一致,內存單元的邏輯

地址仍為「段地址:偏移地址」的形式,為訪問某內存段內的數據,必須使用該段寄存器和存儲單元的偏移地址。

保護方式:在此方式下,情況要復雜得多,裝入段寄存器的不再是段值,而是稱為「選擇子」的某個值。


6、指令指針寄存器


32位CPU把指令指針擴展到32位,並記作EIP,EIP的低16位與先前CPU中的IP作用相同。

指令指針EIP、IP是存放下次將要執行的指令在代碼段的偏移地址,在具有預取指令功能的系統中,下次要執

行的指令通常已被預取到指令隊列中,除非發生轉移情況,所以,在理解它們的功能時不考慮存在指令隊列的情

況。

在實方式下,由於每個段的最大范圍為64KB,所以,EIP的高16位肯定都為0,此時,相當於只用其低16

位的IP來反映程序中的指令的執行次序。


7、標志寄存器

1.運算結果標志位。一共6個,包括:CF進位標志位、PF奇偶標志位、AF輔助進位標志位、ZF零標志位、

SF符號標志位、OF溢出標志位。

2.狀態控制標志位。一共3個,包括:TF追蹤標志位、IF中斷允許標志位、DF方向標志位。

以上標志位在第7章里都講過了,在這里就不再解釋了,現在講講32位標志寄存器增加的4個標志位。

1. I/O特權標志IOPL。

IOPL用兩位二進制位來表示,也稱為I/O特權級欄位,該欄位指定了要求執行I/O指令的特權級,如果當前

的特權級別在數值上小於等於IOPL的值,那麼,該I/O指令可執行,否則將發生一個保護異常。

2. 嵌套任務標志NT。

NT用來控制中斷返回指令IRET的執行。具體規定如下:

(1) 當NT=0,用堆棧中保存的值恢復EFlags、CS和EIP,執行常規的中斷返回操作。

(2) 當NT=1,通過任務轉換實現中斷返回。

3. 重啟動標志RF。

RF用來控制是否接受調試故障。規定:RF=0時,表示接受,否則拒絕。

4. 虛擬8086方式標志VM。

如果VM=1,表示處理機處於虛擬的8086方式下的工作狀態,否則,處理機處於一般保護方式下的工作狀態。


8、32位地址的定址方式

最後說一下32位地址的定址方式。在前面我們學習了16位地址的定址方式,一共有5種,在32位微機系統

中,又提供了一種更靈活、方便但也更復雜的內存定址方式,從而使內存地址的定址范圍得到了進一步擴大。

在用16位寄存器來訪問存儲單元時,只能使用基地址寄存器(BX和BP)和變址寄存器(SI和DI)來作為

偏移地址的一部分,但在用32位寄存器定址時,不存在上述限制,所有32位寄存器(EAX、EBX、ECX、

EDX、ESI、EDI、EBP、和ESP)都可以是偏移地址的一個組成部分。

當用32位地址偏移量進行定址時,偏移地址可分為3部分:

1. 一個32位基址寄存器(EAX、EBX、ECX、EDX、ESI、EDI、EBP、ESP)。

2. 一個可乘以1、2、4、8的32位變址寄存器(EAX、EBX、ECX、EDX、ESI、EDI和EBP)。

3. 一個8位~32位的偏移常量。

比如,指令:mov ebx, [eax+edx*2+300]

Eax就是基址寄存器,edx就是變址寄存器,300H就是偏移常量。

上面那3部分可進行任意組合,省去其中之一或之二。

下面列舉幾個32位地址定址指令:

Mov ax, [123456]

Mov eax, [ebx]

Mov ebx, [ecx*2]

Mov ebx, [eax+100]

Mov ebx, [eax*4+200]

Mov ebx, [eax+edx*2]

Mov ebx, [eax+edx*4+300]

Mov ax, [esp]

由於32位定址方式能使用所有的通用寄存器,所以,和該有效地址相組合的段寄存器也就有新的規定,具體

規定如下:

1. 地址中寄存器的書寫順序決定該寄存器是基址寄存器還是變址寄存器。

如:[ebx+ebp]中的ebx是基址寄存器,ebp是變址寄存器,而[ebp+ebx]中的ebp是基址寄存器,ebx是變

址寄存器,可以看出,左邊那個是基址寄存器,另一個是變址寄存器。

2. 默認段寄存器的選用取決於基址寄存器。

3. 基址寄存器是ebp或esp時,默認的段寄存器是SS,否則,默認的段寄存器是DS。

4. 在指令中,如果顯式地給出段寄存器,那麼顯式段寄存器優先。

下面列舉幾個32位地址定址指令及其內存操作數的段寄存器。

指令列舉: 訪問內存單元所用的段寄存器

mov ax, [123456] ;默認段寄存器為DS。

mov ax, [ebx+ebp] ;默認段寄存器為DS。

mov ebx, [ebp+ebx] ;默認段寄存器為SS。

mov ebx, [eax+100] ;默認段寄存器為DS。

mov edx, ES:[eax*4+200] ;顯式段寄存器為ES。

mov [esp+edx*2], ax ;默認段寄存器為SS。

mov ebx, GS:[eax+edx*8+300] ;顯式段寄存器為GS。

mov ax, [esp] ;默認段寄存器為SS。

9. C++中 什麼是 寄存器

C語言提供了另一種變數,即寄存器變數。這種變數存放在CPU的寄存器中,使用時,不需要訪問內存,而直接從寄存器中讀寫, 這樣可提高效率。寄存器變數的說明符是register。 對於循環次數較多的循環控制變數及循環體內反復使用的變數均可定義為寄存器變數。

[例5.16]

求∑200i=1imain()

{
register i,s=0;
for(i=1;i<=200;i++)
s=s+i;
printf("s=%d\n",s);
}

本程序循環200次,i和s都將頻繁使用,因此可定義為寄存器變數。對寄存器變數還要說明以下幾點:

1. 只有局部自動變數和形式參數才可以定義為寄存器變數。因為寄存器變數屬於動態存儲方式。凡需要採用靜態存儲方式的量不能定義為寄存器變數。

10. 什麼是寄存器什麼是寄存器變數

寄存器是中央處理器內的組成部分。寄存器是有限存貯容量的高速存貯部件,它們可用來暫存指令、數據和位址。在中央處理器的控制部件中,包含的寄存器有指令寄存器(IR)和程序計數器(PC)。在中央處理器的算術及邏輯部件中,包含的寄存器有累加器(ACC)。寄存器是內存階層中的最頂端,也是系統獲得操作資料的最快速途徑。寄存器通常都是以他們可以保存的位元數量來估量,舉例來說,一個 「8 位元寄存器」或 「32 位元寄存器」。寄存器現在都以寄存器檔案的方式來實作,但是他們也可能使用單獨的正反器、高速的核心內存、薄膜內存以及在數種機器上的其他方式來實作出來。 寄存器通常都用來意指由一個指令之輸出或輸入可以直接索引到的暫存器群組。更適當的是稱他們為 「架構寄存器」。寄存器是CPU內部的元件,寄存器擁有非常高的讀寫速度,所以在寄存器之間的數據傳送非常快。
register[寄存器]變數告訴編譯器相關的變數應該改量存儲在高速度的寄存器中。使用register存儲類型的目的一般是為了提高執行速度,但是,register聲明只是向編譯器所提出的「建議」,並非強制要求。

閱讀全文

與編譯器管理存儲的寄存器相關的資料

熱點內容
操作系統代碼編譯 瀏覽:483
程序員東北大學 瀏覽:426
編譯忽略空字元 瀏覽:117
多店鋪阿里雲伺服器教程 瀏覽:378
單片機求初值 瀏覽:420
安卓機如何在電腦備份圖片 瀏覽:925
ca證書加密機價格 瀏覽:798
天乾地支年份演算法 瀏覽:796
程序員打造的視頻 瀏覽:7
java和php通信 瀏覽:680
為什麼黑程序員 瀏覽:163
程序員男生 瀏覽:456
戴爾文件夾內文件怎麼置頂 瀏覽:582
雲伺服器6m網速 瀏覽:722
vivo手機中國聯通伺服器地址 瀏覽:862
工程總控編譯失敗 瀏覽:707
燕趙紅楓app如何下載 瀏覽:867
php查殺軟體 瀏覽:878
教育管理學pdf 瀏覽:547
伺服器均衡怎麼使用 瀏覽:626