乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      Linux x86_64與i386區(qū)別

       mediatv 2012-10-31

      1 引子

       

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

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

       

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

       

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

       

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

       

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

       

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

       

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

       

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

       

      術(shù)語"BSS"已經(jīng)有些年頭了,它是block started by symbol的縮寫。因為未初始化的變量沒有對應(yīng)的值,所以并不需要存儲在可執(zhí)行對象中。但是因為C標(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ā)》)

       

      我們在x86_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個通用寄存器(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ù)邏輯操作、寄存器到內(nèi)存的數(shù)據(jù)傳輸現(xiàn)在都能以64位 的整形類型進(jìn)行操作。堆棧的壓棧和彈出操作都以8字節(jié)的單位進(jìn)行,而且指針類型也擁有了64位。

       

      2)新增寄存器   

       

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

       

      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浮點協(xié)處理器來完成浮點運(yùn)算了。

       

      6)NX位   

       

      跟PAE技術(shù)一樣,新的x86-64架構(gòu)也在頁表項中增加了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)下,仍然對這些機(jī)制進(jìn)行了保留。

       

      3 x86_64段式管理

       

      x86的兩種工作模式:實地址模式和虛地址模式(保護(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)識一個段。這些段寄存器作為段選擇器,用來選擇該段的描述符。

       

      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個字節(jié)。同樣,內(nèi)核數(shù)據(jù)段、用戶代碼段、用戶數(shù)據(jù)段分別存放在

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

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

       

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

       

      例如,為了對內(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)程任然使用相同的虛擬地址,這就是傳說中的“基本平坦模式”。
      按照這個模式,虛擬地址跟線性地址數(shù)字一樣,唯一的不同就是CS和DS裝的內(nèi)容不同,可能是KERNEL級別的選擇子,也可能是USER級別
      的選擇子。

       

      4 x86_64分頁管理

       

      雖然邏輯地址擴(kuò)展到了64位,但是,現(xiàn)有的設(shè)計并沒有完全用到這64位的空間(2^64=16EB),因為使用到如此大的空間,勢必造成很大的

      系統(tǒng)開銷。AMD64在設(shè)計的時候就決定在x86_64的第一階段,只用這64位中的低48位來做頁式地址轉(zhuǎn)換,高16位(48-64位)將填充第 47位相同的內(nèi)容(這種方式類似于符號擴(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è)計帶來了一定便利:可以取地址的上半段保留 做為操作系統(tǒng)的邏輯地址空間,而低地址部分做為裝載應(yīng)用程序的空間,而canonical form不允許的地址空間則做為操作系統(tǒng)的標(biāo)志、以及特權(quán)級的標(biāo)識等。當(dāng)然,這樣的設(shè)計在未來地址進(jìn)一步擴(kuò)展的時候?qū)⒊蔀橐粋€新的問題。

       

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

       

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

       

      x86-64的長模式下,對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沒有什么兩樣。

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

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多