小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

Linux x86_64與i386區(qū)別

 mediatv 2012-10-31

1 引子

 

毫無疑問,不管是32位,還是64位處理器,所有進(jìn)程(執(zhí)行的程序)都必須占用一定數(shù)量的內(nèi)存,它或是用來存放從磁盤載入的程序代碼,或是

存放取自用戶輸入的數(shù)據(jù)等等。不過進(jìn)程對(duì)這些內(nèi)存的管理方式因內(nèi)存用途不一而不盡相同,有些內(nèi)存是事先靜態(tài)分配和統(tǒng)一回收的,而有些卻是按需要?jiǎng)討B(tài)分配和回收的。

 

對(duì)任何一個(gè)普通進(jìn)程來講,它都會(huì)涉及到5種不同的數(shù)據(jù)段。稍有編程知識(shí)的朋友都該能想到這幾個(gè)數(shù)據(jù)段種包含有“程序代碼段”、“程序數(shù)據(jù)段”、“程 序堆棧段”等。不錯(cuò),這幾種數(shù)據(jù)段都在其中,但除了以上幾種數(shù)據(jù)段之外,進(jìn)程還另外包含兩種數(shù)據(jù)段。下面我們來簡單歸納一下進(jìn)程對(duì)應(yīng)的內(nèi)存空間中所包含的 5種不同的數(shù)據(jù)區(qū)。

 

代碼段:代碼段是用來存放可執(zhí)行文件的操作指令,也就是說是它是可執(zhí)行程序在內(nèi)存種的鏡像。代碼段需要防止在運(yùn)行時(shí)被非法修改,所以只準(zhǔn)許讀取操作,而不允許寫入(修改)操作——它是不可寫的。

 

數(shù)據(jù)段:數(shù)據(jù)段用來存放可執(zhí)行文件中已初始化全局變量,換句話說就是存放程序靜態(tài)分配的變量和全局變量。

 

BSS段:BSS段包含了程序中未初始化全局變量,在內(nèi)存中 bss段全部置零。

 

堆(heap):堆是用于存放進(jìn)程運(yùn)行中被動(dòng)態(tài)分配的內(nèi)存段,它大小并不固定,可動(dòng)態(tài)擴(kuò)張或縮減。當(dāng)進(jìn)程調(diào)用malloc等函數(shù)分配內(nèi)存時(shí),新分配的內(nèi)存就被動(dòng)態(tài)添加到堆上(堆被擴(kuò)張);當(dāng)利用free等函數(shù)釋放內(nèi)存時(shí),被釋放的內(nèi)存從堆中被剔除(堆被縮減)。

 

棧:棧是用戶存放程序臨時(shí)創(chuàng)建的局部變量,也就是說我們函數(shù)括弧“{}”中定義的變量(但不包括static聲明的變量,static意味這在數(shù)據(jù) 段中存放變量)。除此以外在函數(shù)被調(diào)用時(shí),其參數(shù)也會(huì)被壓入發(fā)起調(diào)用的進(jìn)程棧中,并且待到調(diào)用結(jié)束后,函數(shù)的返回值也回被存放回棧中。由于棧的先進(jìn)先出特 點(diǎn),所以棧特別方便用來保存/恢復(fù)調(diào)用現(xiàn)場。從這個(gè)意義上將我們可以把堆棧看成一個(gè)臨時(shí)數(shù)據(jù)寄存、交換的內(nèi)存區(qū)。

 

靜態(tài)分配內(nèi)存就是編譯器在編譯程序的時(shí)候根據(jù)源程序來分配內(nèi)存. 動(dòng)態(tài)分配內(nèi)存就是在程序編譯之后, 運(yùn)行時(shí)調(diào)用運(yùn)行時(shí)刻庫函數(shù)來分配內(nèi)存的. 靜態(tài)分配由于是在程序運(yùn)行之前,所以速度快, 效率高, 但是局限性大. 動(dòng)態(tài)分配在程序運(yùn)行時(shí)執(zhí)行, 所以速度慢, 但靈活性高。

 

術(shù)語"BSS"已經(jīng)有些年頭了,它是block started by symbol的縮寫。因?yàn)槲闯跏蓟淖兞繘]有對(duì)應(yīng)的值,所以并不需要存儲(chǔ)在可執(zhí)行對(duì)象中。但是因?yàn)镃標(biāo)準(zhǔn)強(qiáng)制規(guī)定未初始化的全局變量要被賦予特殊的默認(rèn)值 (基本上是0值),所以內(nèi)核要從可執(zhí)行代碼裝入變量(未賦值的)到內(nèi)存中,然后將零頁映射到該片內(nèi)存上,于是這些未初始化變量就被賦予了0值。這樣做避免 了在目標(biāo)文件中進(jìn)行顯式地初始化,減少空間浪費(fèi)(來自《Linux內(nèi)核開發(fā)》)

 

我們?cè)趚86_64環(huán)境上運(yùn)行以下經(jīng)典程序:

#include<stdio.h>
#include<malloc.h>
#include<unistd.h>

int bss_var;
int data_var0=1;

int main(int argc,char **argv)
{
        printf("below are addresses of types of process's mem/n");

        printf("Text location:/n");
        printf("/tAddress of main(Code Segment):%p/n",main);

        printf("____________________________/n");

        int stack_var0=2;

        printf("Stack Location:/n");
        printf("/tInitial end of stack:%p/n",&stack_var0);

        int stack_var1=3;

        printf("/tnew end of stack:%p/n",&stack_var1);

        printf("____________________________/n");

        printf("Data Location:/n");
        printf("/tAddress of data_var(Data Segment):%p/n",&data_var0);

        static int data_var1=4;

        printf("/tNew end of data_var(Data Segment):%p/n",&data_var1);

        printf("____________________________/n");

        printf("BSS Location:/n");
        printf("/tAddress of bss_var:%p/n",&bss_var);

        printf("____________________________/n");

        char *b = sbrk((ptrdiff_t)0);

        printf("Heap Location:/n");
        printf("/tInitial end of heap:%p/n",b);
        brk(b+4);
        b=sbrk((ptrdiff_t)0);

        printf("/tNew end of heap:%p/n",b);

        return 0;

}

 

運(yùn)行結(jié)果:
[root@kollera updilogs]# ./memory
below are addresses of types of process's mem
Text location:
        Address of main(Code Segment):0x400568
____________________________
Stack Location:
        Initial end of stack:0x7fff0e0dc544
        new end of stack:0x7fff0e0dc540
____________________________
Data Location:
        Address of data_var(Data Segment):0x600bfc
        New end of data_var(Data Segment):0x600c00
____________________________
BSS Location:
        Address of bss_var:0x600c14
____________________________
Heap Location:
        Initial end of heap:0xb059000
        New end of heap:0xb059004

 

2 x86_64體系新變化

 

AMD x86_64的出現(xiàn),給全新的64位的x86帶來了很多結(jié)構(gòu)上的變化:   

 

1)64位整型數(shù)   

 

在x86-64中,所有通用寄存器(GPRs)都從32位擴(kuò)充到了64位,名字也發(fā)生了變化。8個(gè)通用寄存器(eax, ebx, ecx, edx,

ebp, esp, esi, edi)在新的結(jié)構(gòu)中被命名為rax, rbx, rcx, rdx, rbp, rsp, rsi, rdi,它們都是64位的。呵呵,想當(dāng)年,從16位擴(kuò)充到32位時(shí),同樣也有一次名字的變化。所有算術(shù)邏輯操作、寄存器到內(nèi)存的數(shù)據(jù)傳輸現(xiàn)在都能以64位 的整形類型進(jìn)行操作。堆棧的壓棧和彈出操作都以8字節(jié)的單位進(jìn)行,而且指針類型也擁有了64位。

 

2)新增寄存器   

 

在新的架構(gòu)中,另外新增了8個(gè)通用寄存器:64位的r8, r9, r10, r11, r12, r13, r14, r15。這樣就有利與編譯器將函數(shù)參數(shù)、返回值等放在這些新增的GPR里面進(jìn)行傳遞,從而提高了程序的運(yùn)行速度。同時(shí),128位的MMX寄存器也從原來的 8個(gè)增加到了16個(gè)。

 

3)增大的邏輯地址空間   

 

目前在新的架構(gòu)中,應(yīng)用程序可以擁有的邏輯地址空間從4GB增加到了256TB(2^48),而且這一邏輯地址空間在未來可能增加到16EB

(2^64,1EB=1024PB,1PB=1024TB,1TB=1024GB)。

 

4)增大的物理地址空間   

 

目前的x86-64架構(gòu),可以支持的物理內(nèi)存擴(kuò)展到了1TB(2^40),當(dāng)然,在未來該數(shù)字可以擴(kuò)展到4PB(2^52)。相比于經(jīng)過PAE技術(shù)擴(kuò)展的i386的64GB物理內(nèi)存,新的架構(gòu)帶來了不小的飛躍。

 

5)無縫使用SSE指令   

 

新的架構(gòu)借鑒和吸收了Intel的SSE、SSE2的核心指令,并在2005年加入了SSE3。在這一新的架構(gòu)下,可以不再需要x87浮點(diǎn)協(xié)處理器來完成浮點(diǎn)運(yùn)算了。

 

6)NX位   

 

跟PAE技術(shù)一樣,新的x86-64架構(gòu)也在頁表項(xiàng)中增加了NX位,來幫助CPU判斷該頁包含的內(nèi)容是否是可以執(zhí)行的,從而避免借助“buffer overrun”導(dǎo)致的病毒攻擊。

 

7)去除舊的機(jī)制   

 

在新架構(gòu)的“長模式(long mode)”下,很多在IA32中被提出,但確不經(jīng)常被操作系統(tǒng)用到的一些機(jī)制不再被支持。這些機(jī)制包括段式地址變化機(jī)制(FS和GS仍然被保留),任務(wù) 轉(zhuǎn)移門(TSS)機(jī)制,以及虛擬86模式。當(dāng)然,出于向下兼容的考慮,x86-64在“傳統(tǒng)模式”(Legacy mode)下,仍然對(duì)這些機(jī)制進(jìn)行了保留。

 

3 x86_64段式管理

 

x86的兩種工作模式:實(shí)地址模式和虛地址模式(保護(hù)模式)。Linux主要工作在保護(hù)模式下。

 

在保護(hù)模式下,64位x86體系架構(gòu)的虛地址空間可達(dá)2^48Byte,即256TB,這可比只能到達(dá)區(qū)區(qū)4GB的32位x86體系大多了。邏輯地 址到線性地址的轉(zhuǎn)換由x86分段機(jī)制管理。段寄存器CS、DS、ES、SS、FS或GS各標(biāo)識(shí)一個(gè)段。這些段寄存器作為段選擇器,用來選擇該段的描述符。

 

Linux中關(guān)于段描述符的宏定義集中在文件/arch/x86/include/asm/Segment.h中,我們先貼出部分代碼:

 

32位的:

 

#define GDT_ENTRY_KERNEL_BASE 12                             /* 0x0000000c c=>1100*/
#define GDT_ENTRY_KERNEL_CS (GDT_ENTRY_KERNEL_BASE + 0)      /* 0x0000000c c=>1100*/
#define GDT_ENTRY_KERNEL_DS (GDT_ENTRY_KERNEL_BASE + 1)      /* 0x0000000d c=>1101*/

 

64位的:

 

#define GDT_ENTRY_KERNEL32_CS 1         /* 0x00000001 */
#define GDT_ENTRY_KERNEL_CS 2           /* 0x00000002 */
#define GDT_ENTRY_KERNEL_DS 3           /* 0x00000003 */

#define __KERNEL32_CS   (GDT_ENTRY_KERNEL32_CS * 8)          /* 0x00000100 */

#define GDT_ENTRY_DEFAULT_USER32_CS 4   /* 0x00000004 */
#define GDT_ENTRY_DEFAULT_USER_DS 5     /* 0x00000005 */
#define GDT_ENTRY_DEFAULT_USER_CS 6     /* 0x00000006 */

#define __USER32_CS   (GDT_ENTRY_DEFAULT_USER32_CS * 8 + 3)  /* 0x00000403 */
#define __USER32_DS __USER_DS

 

不管32位還是64位的:(我們只關(guān)心64位)

 

#define __KERNEL_CS (GDT_ENTRY_KERNEL_CS * 8)            /* 0x00000200 */
#define __KERNEL_DS (GDT_ENTRY_KERNEL_DS * 8)            /* 0x00000300 */
#define __USER_DS     (GDT_ENTRY_DEFAULT_USER_DS* 8 + 3)     /* 0x00000503 */
#define __USER_CS     (GDT_ENTRY_DEFAULT_USER_CS* 8 + 3)     /* 0x00000603 */

 

看見沒有,我們熟悉的__USER_CS,__USER_DS,__KERNEL_CS,和__KERNEL_DS,就是傳說中的段選擇子。

 

我們看到,內(nèi)核代碼段的描述子存放在以0x200為基地址的內(nèi)存單元中,占8個(gè)字節(jié)。同樣,內(nèi)核數(shù)據(jù)段、用戶代碼段、用戶數(shù)據(jù)段分別存放在

以0x300、0x500、0x600為基地址的內(nèi)存單元中。我們注意到,__USER_DS和__USER_CS的最低三位為3,也就是011,這正說明

其CPL位為11,代表用戶模式,TI為0,代表GDT。

 

對(duì)于x86_64來說,虛擬地址由16位選擇子和64位偏移量組成,段寄存器僅僅存放選擇子。CPU的分段單元(SU)執(zhí)行以下操作:
[1] 先檢查選擇子的TI字段,以決定描述子對(duì)應(yīng)的描述子保存在哪一個(gè)描述符表中。TI字段指明描述子是在GDT中(在這種情況下,分段單元從gdtr寄存器中 得到GDT的線性基地址)還是在激活的LDT中(在這種情況下,分段單元從ldtr寄存器中得到LDT的線性基地址)。
[2] 從選擇子的13位index字段計(jì)算描述子的地址,index字段的值乘以8(一個(gè)描述子的大小,其實(shí)就是屏蔽掉末尾那三位指示特權(quán)級(jí)的CPL和指示TI的字段),這個(gè)結(jié)果與gdtr或ldtr寄存器中的內(nèi)容相加。
[3] 將對(duì)應(yīng)的段描述子從內(nèi)存拷貝到CPU的影子Cache中,這樣,只有在選擇子改變的情況下才會(huì)修改影子Cache中的內(nèi)容。
[4] 把虛擬地址的偏移量與隱Cache中描述子Base字段的值相加就得到了線性地址。

 

例如,為了對(duì)內(nèi)核代碼段尋址,內(nèi)核只需要把__KERNEL_CS宏產(chǎn)生的選擇子的值裝進(jìn)cs段寄存器即可。注意,與段相關(guān)的線性地址還是從

0開始,達(dá)到264 -1的尋址限長。這就意味著在用戶態(tài)或內(nèi)核態(tài)下的所有進(jìn)程任然使用相同的虛擬地址,這就是傳說中的“基本平坦模式”。
按照這個(gè)模式,虛擬地址跟線性地址數(shù)字一樣,唯一的不同就是CS和DS裝的內(nèi)容不同,可能是KERNEL級(jí)別的選擇子,也可能是USER級(jí)別
的選擇子。

 

4 x86_64分頁管理

 

雖然邏輯地址擴(kuò)展到了64位,但是,現(xiàn)有的設(shè)計(jì)并沒有完全用到這64位的空間(2^64=16EB),因?yàn)槭褂玫饺绱舜蟮目臻g,勢(shì)必造成很大的

系統(tǒng)開銷。AMD64在設(shè)計(jì)的時(shí)候就決定在x86_64的第一階段,只用這64位中的低48位來做頁式地址轉(zhuǎn)換,高16位(48-64位)將填充第 47位相同的內(nèi)容(這種方式類似于符號(hào)擴(kuò)展)。如果邏輯地址不符合此規(guī)定,系統(tǒng)將產(chǎn)生異常。符合此規(guī)定的地址稱為canonical form,地址的范圍分為兩段:0 到 00007FFF-FFFFFFFF,以及FFFF8 000-0000 0000到FFFFFFFF-FFFFFFFF,總共為256TB。這種虛擬地址的分層結(jié)構(gòu),也為操作系統(tǒng)的設(shè)計(jì)帶來了一定便利:可以取地址的上半段保留 做為操作系統(tǒng)的邏輯地址空間,而低地址部分做為裝載應(yīng)用程序的空間,而canonical form不允許的地址空間則做為操作系統(tǒng)的標(biāo)志、以及特權(quán)級(jí)的標(biāo)識(shí)等。當(dāng)然,這樣的設(shè)計(jì)在未來地址進(jìn)一步擴(kuò)展的時(shí)候?qū)⒊蔀橐粋€(gè)新的問題。

 

采用64位地址空間的x86-86被稱為是運(yùn)行在“長模式”(long mode)下,該模式可以看成是對(duì)PAE模式的一個(gè)擴(kuò)充。長模式允許使用三個(gè)不同的物理頁面大小:4KB、2MB和1GB。在使用64位中的48位用來存 放地址時(shí),與PAE模式下的三級(jí)頁面映射機(jī)制不同的是,長模式下線性地址到物理地址的映射需要經(jīng)過四級(jí)地址映射。在這四級(jí)地址映射機(jī)制中,原來PAE模式 下僅擁有4個(gè)表項(xiàng)的頁目錄指針表被擴(kuò)展到512個(gè)表項(xiàng)。同時(shí),在最末一級(jí)加入一級(jí)新的頁面映射結(jié)構(gòu),該結(jié)構(gòu)被稱為第四級(jí)頁表(Page-Map Level 4 Table,PML4),它跟PAE模式下的頁目錄及頁表(在長模式中,成為了頁目錄)一樣,擁有512個(gè)表項(xiàng)。如果地址進(jìn)一步擴(kuò)充,如把64位尋址全部 用上,該頁表就能夠擴(kuò)充到33,554,432個(gè)表項(xiàng),或者干脆再加一層地址映射(PML5),當(dāng)然,按照目前只用了48位的情況下,用到512個(gè)表項(xiàng)的 PML4就已經(jīng)夠用了。   

 

可以想象,用到48位的x86-64虛擬地址的分配機(jī)制為:   
- 0-11(12)位:頁內(nèi)偏移;   
- 12-20(9)位:由PML4來映射;   
- 21-29(9)位:高一級(jí)頁目錄來映射(如果PS=1,則該頁表項(xiàng)指向一個(gè)2MB的頁);   
- 30-38(9)位:再高一級(jí)的頁目錄來映射(如果PS=2,則該頁表項(xiàng)指向一個(gè)1GB的頁);   
- 39-47(9)位:頁目錄指針表來映射。   

 

x86-64的長模式下,對(duì)16位以及32位代碼進(jìn)行了兼容,即使CPU上跑的是64位的操作系統(tǒng),歷史遺留的16位以及32位代碼將都能夠在該操作系統(tǒng)上運(yùn)行。由于x86-64兼容IA32的指令,所以,這些代碼在這種情況下運(yùn)行,基本上沒有性能損耗。   

 

在傳統(tǒng)模式(Legacy mode)下,x86-64的CPU的工作模式跟傳統(tǒng)的IA32沒有什么兩樣。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多