天天看點

(整理)使用者空間_核心空間以及記憶體映射

核心空間和使用者空間

  現代作業系統采用虛拟存儲器,對于32位作業系統而言,它的尋址空間(虛拟存儲空間)為4G(2的32次方)。作業系統的核心是核心,獨立于普通的應用程式,可以通路受保護的記憶體空間,也有通路底層硬體裝置的權限。為了保證使用者程序不能直接操作核心,保證核心安全,作業系統将虛拟空間劃分為兩部分,一部分是核心空間,一部分是使用者空間。針對Linux作業系統,将最高的1G位元組(從虛拟位址0xC0000000到0xFFFFFFFF)供核心使用,稱為核心空間,而較低的3G位元組(從虛拟位址0x00000000到0xBFFFFFFF),供各個程序使用,稱為使用者空間。每個程序都可以通過系統調用進入到核心。其中在Linux系統中,程序的使用者空間是獨立的,而核心空間是共有的,程序切換時,使用者空間切換,核心空間不變。

(整理)使用者空間_核心空間以及記憶體映射

  有了使用者空間和核心空間的劃分後,整個linux内部結構可以分為三部分,從最底層到最上層依次是:硬體->核心空間->使用者空間,如下圖所示:

(整理)使用者空間_核心空間以及記憶體映射

使用者态和核心态

  當一個程序執行系統調用而陷入核心代碼中執行時,稱進行處于核心運作态(核心态)。此時處理器處于特權級别最高的(0級)核心代碼中執行。當程序處于核心态時,執行的核心代碼會使用目前程序的核心棧。每個程序都有自己的核心棧。

  當進行在執行使用者自己的代碼時,則稱其處于使用者運作态(使用者态)。此時處理器在特權級最低的(3級)使用者代碼中運作。當正在執行使用者程式而突然被中斷程式中斷時,此時使用者程式也可象征性的稱為處于進行的核心态,因為中斷處理程式使用目前程序的核心棧。

聲明:以上兩部分整理自:http://www.cnblogs.com/Anker/p/3269106.html

邏輯位址、線性位址和實體位址

  在解釋高端記憶體和記憶體映射前,先複習一下什麼是邏輯位址、線性位址和實體位址吧,大家要是知道的話就可以直接跳過。

邏輯位址

  邏輯位址(Logical Address) 是指由程式産生的和段相關的偏移位址部分。例如,你在進行C語言指針程式設計中,能讀取指針變量本身值(&操作),實際上這個值就是邏輯位址,他是相對于你目前程序資料段的位址,不和絕對實體位址相幹。隻有在Intel實模式下,邏輯位址才和實體位址相等(因為實模式沒有分段或分頁機制,Cpu不進行自動位址轉換);邏輯也就是在Intel保護模式下程式執行代碼段限長内的偏移位址(假定代碼段、資料段如果完全相同)。應用程式員僅需和邏輯位址打交道,而分段和分頁機制對你來說是完全透明的,僅由系統程式設計人員涉及。應用程式員雖然自己能直接操作記憶體,那也隻能在作業系統給你配置設定的記憶體段操作。

線性位址

線性位址(Linear Address) 是邏輯位址到實體位址變換之間的中間層。程式代碼會産生邏輯位址,或說是段中的偏移位址,加上相應段的基位址就生成了一個線性位址。如果啟用了分頁機制,那麼線性位址能再經變換以産生一個實體位址。若沒有啟用分頁機制,那麼線性位址直接就是實體位址。Intel 80386的線性位址空間容量為4G(2的32次方即32根位址總線尋址)。

實體位址

實體位址(Physical Address) 是指出目前CPU外部位址總線上的尋址實體記憶體的位址信号,是位址變換的最終結果位址。如果啟用了分頁機制,那麼線性位址會使用頁目錄和頁表中的項變換成實體位址。如果沒有啟用分頁機制,那麼線性位址就直接成為實體位址了。

虛拟位址

  虛拟記憶體(Virtual Memory)是指計算機呈現出要比實際擁有的記憶體大得多的記憶體量。是以他允許程式員編制并運作比實際系統擁有的記憶體大得多的程式。這使得許多大型項目也能夠在具有有限記憶體資源的系統上實作。一個非常恰當的比喻是:你不必非常長的軌道就能讓一列火車從上海開到北京。你隻需要足夠長的鐵軌(比如說3公裡)就能完成這個任務。采取的方法是把後面的鐵軌即時鋪到火車的前面,隻要你的操作足夠快并能滿足需求,列車就能象在一條完整的軌道上運作。這也就是虛拟記憶體管理需要完成的任務。在Linux0.11核心中,給每個程式(程序)都劃分了總容量為64MB的虛拟記憶體空間。是以程式的邏輯位址範圍是0x0000000到0x4000000。有時我們也把邏輯位址稱為 虛拟位址。因為和虛拟記憶體空間的概念類似,邏輯位址也是和實際實體記憶體容量無關的。邏輯位址和實體位址的“差距”是0xC0000000,是由于虛拟位址->線性位址->實體位址映射正好差這個值。這個值是由作業系統指定的。機理 邏輯位址(或稱為虛拟位址)到線性位址是由CPU的段機制自動轉換的。如果沒有開啟分頁管理,則線性位址就是實體位址。如果開啟了分頁管理,那麼系統程式需要參和線性位址到實體位址的轉換過程。具體是通過設定頁目錄表和頁表項進行的。

聲明:以上部分摘自:http://blog.csdn.net/do2jiang/article/details/4512417

高端記憶體

高端記憶體的由來

  在傳統的Linux x86 32位系統中,核心子產品的代碼或者線程通路記憶體時,代碼中的記憶體位址都為邏輯位址,而對應到真正的實體記憶體位址時,還需要位址的一一映射。如果邏輯位址位0xC0000003,那麼對應的實體位址就是0x3,如果邏輯位址位0xC0000004,那麼對應的實體位址就是0x4,是以實體位址和邏輯位址的關系如下:

實體位址 = 邏輯位址 – 0xC0000000
      

  根據上面的核心位址空間的位址轉換關系,注意核心的虛拟位址在“高端”,但是ta映射的實體記憶體位址在低端。會發現,核心子產品能夠通路的邏輯位址為0xC0000000-0xFFFFFFFF,對應的實體位址為0x00000000-0x40000000,總共1G的記憶體。也就是說如果計算機的總實體記憶體大于1G,按照上面的映射關系,高于1G的部分,核心就無法通路到了。為了解決這種狀況,就出現了高端記憶體一說。

  因為不能直接将内和空間的1G記憶體直接做一一映射,是以Linux核心将核心空間分成了三個部分,分别是:ZONE_DMA,ZONE_NORMAL和ZONE_HIGHMEM。這三個區域的記憶體配置設定情況如下:

ZONE_DMA 記憶體開始的16MB空間
ZONE_NORMAL 16MB-896MB
ZONE_HIGHMEM 896MB-結束(1G)

對高端記憶體的了解

  上一小節就說到高端記憶體是用來解決核心無法通路大于1G記憶體位址空間的問題的。那麼具體是怎麼實作的呢?總的來說非常簡單,當核心需要通路高于1G的記憶體空間的時候,例如核心需要通路0x50000000-0x500FFFFF這1MB記憶體空間的時候,隻需要在ZONE_HIGHMEM這一個區域内臨時申請一個1MB的記憶體空間,然後将其映射到上述需要通路的記憶體區域即可。當核心使用完後,釋放申請的1MB記憶體空間便完成對高于1G記憶體空間的通路了。

記憶體映射(mmap)

mmap基本概念

  mmap是一種記憶體映射檔案的方法,即将一個檔案或者其它對象映射到程序的位址空間,實作檔案磁盤位址和程序虛拟位址空間中一段虛拟位址的一一對映關系。實作這樣的映射關系後,程序就可以采用指針的方式讀寫操作這一段記憶體,而系統會自動回寫髒頁面到對應的檔案磁盤上,即完成了對檔案的操作而不必再調用read,write等系統調用函數。相反,核心空間對這段區域的修改也直接反映使用者空間,進而可以實作不同程序間的檔案共享。如下圖所示:

(整理)使用者空間_核心空間以及記憶體映射

  由上圖可以看出,程序的虛拟位址空間,由多個虛拟記憶體區域構成。虛拟記憶體區域是程序的虛拟位址空間中的一個同質區間,即具有同樣特性的連續位址範圍。上圖中所示的text資料段(代碼段)、初始資料段、BSS資料段、堆、棧和記憶體映射,都是一個獨立的虛拟記憶體區域。而為記憶體映射服務的位址空間處在堆棧之間的空餘部分。

  linux核心使用vm_area_struct結構來表示一個獨立的虛拟記憶體區域,由于每個不同質的虛拟記憶體區域功能和内部機制都不同,是以一個程序使用多個vm_area_struct結構來分别表示不同類型的虛拟記憶體區域。各個vm_area_struct結構使用連結清單或者樹形結構連結,友善程序快速通路,如下圖所示:

(整理)使用者空間_核心空間以及記憶體映射

  vm_area_struct結構中包含區域起始和終止位址以及其他相關資訊,同時也包含一個vm_ops指針,其内部可引出所有針對這個區域可以使用的系統調用函數。這樣,程序對某一虛拟記憶體區域的任何操作需要用要的資訊,都可以從vm_area_struct中獲得。mmap函數就是要建立一個新的vm_area_struct結構,并将其與檔案的實體磁盤位址相連。具體步驟請看下一節。

mmap記憶體映射原理

mmap記憶體映射的實作過程,總的來說可以分為三個階段:

程序啟動映射過程,并在虛拟位址空間中為映射建立虛拟映射區域

  • 程序在使用者空間調用庫函數mmap,原型:void mmap(void start, size_t length, int prot, int flags, int fd, off_t offset);
  • 在目前程序的虛拟位址空間中,尋找一段空閑的滿足要求的連續的虛拟位址
  • 為此虛拟區配置設定一個vm_area_struct結構,接着對這個結構的各個域進行了初始化
  • 将建立的虛拟區結構(vm_area_struct)插入程序的虛拟位址區域連結清單或樹中

調用核心空間的系統調用函數mmap(不同于使用者空間函數),實作檔案實體位址和程序虛拟位址的一一映射關系

  • 為映射配置設定了新的虛拟位址區域後,通過待映射的檔案指針,在檔案描述符表中找到對應的檔案描述符,通過檔案描述符,連結到核心“已打開檔案集”中該檔案的檔案結構體(struct file),每個檔案結構體維護着和這個已打開檔案相關各項資訊。
  • 通過該檔案的檔案結構體,連結到file_operations子產品,調用核心函數mmap,其原型為:int mmap(struct file filp, struct vm_area_struct vma),不同于使用者空間庫函數。
  • 核心mmap函數通過虛拟檔案系統inode子產品定位到檔案磁盤實體位址。
  • 通過remap_pfn_range函數建立頁表,即實作了檔案位址和虛拟位址區域的映射關系。此時,這片虛拟位址并沒有任何資料關聯到主存中。

程序發起對這片映射空間的通路,引發缺頁異常,實作檔案内容到實體記憶體(主存)的拷貝

注:前兩個階段僅在于建立虛拟區間并完成位址映射,但是并沒有将任何檔案資料的拷貝至主存。真正的檔案讀取是當程序發起讀或寫操作時。

  • 程的讀或寫操作通路虛拟位址空間這一段映射位址,通過查詢頁表,發現這一段位址并不在實體頁面上。因為目前隻建立了位址映射,真正的硬碟資料還沒有拷貝到記憶體中,是以引發缺頁異常。
  • 缺頁異常進行一系列判斷,确定無非法操作後,核心發起請求調頁過程。
  • 調頁過程先在交換緩存空間(swap cache)中尋找需要通路的記憶體頁,如果沒有則調用nopage函數把所缺的頁從磁盤裝入到主存中。
  • 之後程序即可對這片主存進行讀或者寫的操作,如果寫操作改變了其内容,一定時間後系統會自動回寫髒頁面到對應磁盤位址,也即完成了寫入到檔案的過程。

注:修改過的髒頁面并不會立即更新回檔案中,而是有一段時間的延遲,可以調用msync()來強制同步, 這樣所寫的内容就能立即儲存到檔案裡了。

聲明:以上一章内容摘自:http://www.cnblogs.com/huxiao-tee/p/4660352.html

vm_struct和vm_area_struct

  關于vm_struct和vm_area_struct這兩個結構體,需要簡單說明一下,vm_struct和vm_area_struct都是用于表示一片連續的虛拟位址空間的,但是映射到實體位址空間後可以是不連續的。其次,vm_area_struct表示的虛拟位址是給程序使用的,而vm_struct表示的虛拟位址是給核心使用的。從上面的内容可以知道,核心空間的位址分成三個部分,ZONE_DMA、ZONE_NORMAL和ZONE_HIGHMEM,其中前面兩部分是用來和實體位址進行一一映射的,而ZONE_HIGHMEM通過臨時借用以及映射的方法管理高于1G的記憶體,vm_struct所使用的核心虛拟位址就是ZONE_HIGHMEM部分位址。

http://blog4jimmy.com/2018/01/348.html