看《Windows彙編》的一些筆記(記憶體管理篇)

來源:互聯網
上載者:User

     這幾天在看《Windows環境下32位組譯工具設計》,接觸到了一些電腦底層的東西,是我以前瞭解不到的,由於知識比較繁煩,所以特把一些東西記錄下來,方便以後查閱。
      在DOS中,彙編中最先開始接觸的就是記憶體定址了,DOS狀態下,作業系統的記憶體管理與Windows的記憶體管理有著截然不同的方法。其實,說白了,Win32編程相對於DOS編程最大的不同就是記憶體的使用。
      對於DOS來說,是運行於實模式下的,實模式是相對於8086的機器,定址範圍只有1M的空間,也就是00000H-FFFFFH,凡獲得的地址值都不可在這個界區之外。而對於這短短的1M空間來說,系統硬體地址的安排又佔去了記憶體高端的一些空間(從A0000H-FDC000H,計384KB),在系統記憶體的低端,安排了中斷向量表和BIOS資料區(000000H-000500H,計1K位元組),剩下的不到640K就是作業系統和應用程式所共用的記憶體(此謂著名的640KB記憶體限制)。
      DOS下的定址是根據段寄存器DS×10h+段位移得出來的,得出來的值就是實體記憶體中相應地址的值。如XXXX:YYYY格式的虛擬位址在記憶體中的實際位置就是XXXX*10h+YYYY。

       Windows的記憶體管理和DOS記憶體管理有著很大的不同,Windows可以運行在實模式,保護模式,虛擬8086模式下,當運行在實模式下時,定址是跟DOS沒有任何分別的,定址為1M空間,但是在保護模式與虛擬8086模式下,則有著本質的不同,因為在這些模式下,32位的地址線可訪問達4GB的記憶體位址,以前的段地址加段位移無法覆蓋如此大的空間。因為在386的結構中,寄存器已擴至32位,所以,寄存器的值就可以直接表示記憶體中某個地址,而無需用一個段寄存器來作間接定址。
       那麼,在386模式下,段寄存器起什麼作用呢?它不是用作基址,而是用作段選取器,在保護模式下,一個地址空間是否可以被寫入,可以被多少優先順序寫入,是不是允許執行等保護的問題都是需要考慮的。所以,必須對一個地址空間定義一些安全上的屬性,段描述符就是被派上作此用途。段描述符是64位,但在386中,段寄存器卻是16位,空間不夠,放不下。解決辦法是把段描述符組成一個表結構,而用段寄存器來作索引。所以,在386中,段寄存器也叫做了段選取器。
      在386中,引入了兩個新的寄存器來管理段描述符,一個是48位的全域描述表寄存器GDTR,一個是16位的局部描述表寄存器LDTR。在windows中,記憶體的表示是以xxxx:yyyyyyyy表示,下面介紹一下他是怎麼定址的:
      首先,xxxx是無法表示段的基址的,對於這個地址,首先要看xxxx的TI位是否為0(即xxxx的第二位),如果是,則從GDTR中擷取GDT的基址,然後在GDT中以段選取器xxxx的高12位得出索引,根據索引位移找到相應的段描述符,段描述符包括段的基址,限長,優先順序各種屬性,這就得到了段的起始地址,加上yyyyyyyy即是要找的記憶體的線性地址zzzzzzzz。  如果TI位為1,則表示段描述符放在LDT中,第一步的操作還是從GDTR中擷取GDT的基址,然後從LDTR寄存器獲得索引(非XXXX的高十二位),注意,這時根據索引位移得到得並不是段描述符,而是得到LDT段的位置,然後根據xxxx的高十二位從LDT段中獲得段描述。再以這個段描述符資訊得到段基址,再加上位移yyyyyyyy得到要找的線性地址zzzzzzzz。可以寫個簡單的類比程式來表示:

       if( xxxx的第二位==1 ) //段描述符的位置在GDTR中
       {
           A1=(GDTR的前三十二位); //把GDTR的基址給A1
           段描述符=A1+(xxxx的高12位);  //可獲得段描述符
           線性地址 = 段描述符中的基址+yyyyyyyy;
        }
        else  //TI位為0,表示在段描述符在LDT中
        {
           A1=(GDTR的前三十二位);
           A2=A1+LDTR;  //A2即是LDT描述符表的入口,註:LDTR是16位的
           段描述符=A2+(xxxx的高十二位) //LDT描述符表入口加上位移,即是相應的段描述符
     線性地址 = 段描述符中的基址+yyyyyyyy;
         }

       得出線性地址之後,是否就是實體記憶體中相應地址的值呢?那要看是否啟用分頁機制了。如果沒有啟用分頁機制,則線性地址就是相應的物理地址。而如果啟用分頁機制,則不代表相應的物理地址。還需要經過一番折騰才能得到相應的實際物理地址,從而把裡面相應的值給取出來。
       80386啟用分頁機制,可以很好的解決記憶體片段的問題。以前的程式,都是在連續的實體記憶體空間中的存在的,這樣,經過一段時間後,多個程式的建立與退出,則會出現空閑記憶體的總和很大,而任何一片連續的記憶體都小到無法裝了執行程式的地步,這就是記憶體片段。386把4KB大小的一塊記憶體當做一頁記憶體,每頁實體記憶體可以根據頁目錄與頁表,而隨意映射到不同的線性地址上。這樣,就可以將物理地址不連續的記憶體映射到一起,線上性地址上被視為連續的地址空間。在386中,除了CR3使用了物理地址,其餘的都是用線性地址表示記憶體的。
       386中是否啟用分頁是以CR0寄存器中的位32(PG位)表示的,如果PG=0,則分頁機制不啟用,反之則啟用。記憶體分頁只可在保護模式下實現。
        從實體記憶體中的層次看,Windows作業系統和DOS一樣,也是所有的內容共用記憶體,如作業系統使用的代碼和資料,但是從應用程式代碼的層次看,則Windows被看作一個分時的多任務的作業系統,CPU被分成一個個的時間片,分配給不同的應用程式,在一個程式的時間片中,和這個程式執行無關的東西(如其它程式的代碼和資料),並不被映射到相應的線性地址中去,這樣,各個程式就獨立開了,在這個應用程式的線性地址內,並不能訪問別的程式所使用的線性地址空間。
       書上提出了一些Win32編程中的一些重要概念:

      1.每個應用程式都有自己的4GB的定址空間,該空間可以存放作業系統,系統DLL和使用者DLL的代碼,它們之中有各種函數供應用程式調用。再除去一些其它的空間,餘下的就是應用程式的代碼,資料和可以分配的地址空間。
       2.不同的應用程式的線性地址空間是隔離的,雖然它們在實體記憶體中同時存在,但是在某個程式的時間片中,其它應用程式的代碼和資料沒有被映射到相應的可定址的線性地址中,所以是不可訪問的。程式可以使用自己的4GB的定址空間,這個空間完全是程式私人的。
       3.DLL程式沒有自己的私人空間,它們總是被映射到其它應用程式的地址空間中,當做其它應用程式一起運行。原因很簡單,如果它不和其它程式同屬一個地址空間,應用程式如何才能調用得到它??

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.