Linux內存地址管理

文章目錄

  • 系統內存布局
    • 內核地址的低端和高端內存概念
      • 低端內存
      • 高端內存
  • 地址轉換和MMU
    • Linux中的四級分頁模型
      • 虛擬地址字段
      • 頁表處理
  • 將虛擬地址轉換物理地址

Linux系統中的每個內存地址都是虛擬的,它們不直接指向任何物理內存地址。每當訪問內存位置時,可以執行轉換機制以匹配相應的物理內存,所以我們在程序中必須用虛擬地址來訪問數據。

注:下面所說的內核空間和用戶空間這樣的術語指的都是虛擬地址空間

系統內存布局

在Linux系統中,每個進程都有自己獨立的虛擬地址空間。它是一種內存沙箱,存在于進程的生命周期中。在32位系統上,該地址空間大小是4GB。針對每一個進程,4GB的地址空間被分割成兩個部分:

  • 內核空間虛擬地址
  • 用戶空間虛擬地址

分割方式依賴于特殊的內核配置選項CONFIG_PAGE_OFFSET,這個選項定義內核虛擬地址部分在進程地址空間的起始位置。典型的進程虛擬地址空間布局如下圖:
在這里插入圖片描述
內核空間和用戶空間所使用的地址都是虛擬地址,不同的是,訪問內核空間地址需要特權模式,當CPU運行用戶空間代碼時,活動進程被認為運行在用戶模式下,當CPU運行內核空間代碼時,活動進程被認為運行在內核模式下。

內核與每個進程共享其地址空間,原因如下:因為每個進程在給定的時刻都使用系統調用,這將涉及內核。將內核的虛擬內存地址映射到每個進程的虛擬地址空間能夠避免每次進入(或者退出)內核時內存地址切換產生的開銷。這就是內核地址空間被永久映射到每個進程頂部的原因—加快系統調用對內核的訪問。

每個進程地址空間頂部都是內核的虛擬地址空間,這一部分每個進程都是相同的。

內存管理單元把內存組織為大小固定的單元—頁面,內存頁(虛擬頁)指的是連續虛擬內存塊,內核數據結構也使用相同的名稱頁面來表示內存頁。幀(頁面幀)指一段固定長度的連續物理內存塊,操作系統在其上映射內存頁。每個頁面幀都有一個號碼,叫做頁面幀號(PFN)。

內核地址的低端和高端內存概念

Linux內核具有自己的虛擬地址空間。比如32位的x86,內核的虛擬地址空間是1GB大小,分成兩個部分:

  • 低端內存或LOWMEM:第一個896MB
  • 高端內存或HIGHMEM:頂部的128MB

在這里插入圖片描述

低端內存

內核地址空間的第一個896MB空間構成低端內存區域。在啟動早期,內核永久映射這896MB的空間。該映射產生的地址為邏輯地址,這些都是虛擬地址,但是減去固定的偏移量后就可以將其轉換為物理地址。因為映射是永久的,并且事先知道。大多數內核內存函數返回低端內存。事實上,為了滿足不同的用途,內核內存被劃分為區域,LOWMEM的第一個16MB內存保留為DMA使用。內核空間可以確定3種不同的內存區域:

  • ZONE_DMA:包含的內存頁面幀在0~16MB,用于直接內存訪問(DMA)
  • ZONE_NORMAL:包含的內存頁面幀為16MB~896MB,常規使用
  • ZONE_HIGHMEM:包含的內存頁面幀位于896MB及其以上

這就是說,512MB的系統上,不存在以上的劃分。

邏輯地址的另一個定義:線性映射到物理地址上的內核空間中的地址,可以用偏移量或者應用位掩碼將其轉為物理地址,使用__pa(地址)宏可以將邏輯地址(內核中的虛擬地址)轉換為物理地址,使用__va(地址)可以做相反的操作。

高端內存

內核地址空間頂部頂部128MB稱為高端內存區域,內核用它臨時映射1GB以上的物理內存,當需要訪問896MB以上的物理內存時,內核會使用這128MB創建到其虛擬地址空間的臨時映射,也就是將需要訪問數據的物理頁映射到這128MB內核虛擬地址空間來,從而實現訪問所有物理頁面的目標。可以把高端內存定義為邏輯地址存在的內存,但不會將其永久映射到內核地址空間。896MB以上的物理內存按需映射到HIGHMEM區域的128MB。

訪問高端內存的映射由內核動態創建,訪問后銷毀,這使高內存訪問速度變慢,64位系統上不存在高端內存這一概念。

地址轉換和MMU

每次訪問內存位置時,由CPU完成從虛擬地址到物理地址的轉換。該機制稱為地址轉換,這由CPU中的內存管理單元(MMU)來執行。MMU轉換的都是虛擬地址,所以訪問數據,必須是虛擬地址,不能是物理地址,否則訪問不了數據。

對于虛擬內存,內存組織為固定大小的頁,而物理內存則按幀組織,頁面表(PTE)概念的引入是為了管理頁面和幀之間的映射。頁面分部在表間,因此每個PTE的表項對于一個頁面和幀之間的映射,然后給每個進程一組頁面表來描述其整個內存空間。

Linux中的四級分頁模型

Linux采用了一種同時適用于32位和64位系統的普通分頁模型。從2.6.11版本開始,采用了四級分頁模型:請添加圖片描述
上圖展示的4種頁表分別被稱為:

  • 頁全局目錄(Page Global Directory,PGD)
  • 頁上級目錄(Page Upper Directory,PUD)
  • 頁中間目錄(Page Middle Directory,PMD)
  • 頁表(Page Table,PTE)

虛擬地址被分為5個部分,每個頁表項指向一個頁框,每一部分的大小與具體的計算機體系結構有關。

MMU如何知道進程頁面表?很簡單,MMU不存儲任何地址。但CPU有一個特殊的寄存器,稱為頁面表基址寄存器(PTBR)或轉換基址寄存器0(TTBR0),它指向進程1級頁面表(PGD)的基址。這正是struct mm_struct的字段pgd指向的地址:current->mm.pgd == TTBR0.上面圖中的cr3保存的就是該值

虛擬地址字段

下面宏簡化了頁表處理:

  • PAGE_SHIFT:指定Offset字段的位數,這個宏由PAGE_SIZE使用返回頁的大小。最后,PAGE_MASK宏用以屏蔽Offset字段的所有位。
  • PMD_SHIFT:指定虛擬地址的offset字段和table字段的總位數,PMD_MASK宏用于屏蔽Offset字段與Table字段的所有位
  • PUD_SHIFT:確定頁上級目錄項能映射的區域大小的對數,PUD_MASK宏用于屏蔽Offset字段,Table字段、Middle Air字段和Upper Air字段的所有位
  • PGDIR_SHIFT:確定頁全局目錄項能映射的區域大小的對數。PGDIR_MASK宏用于屏蔽Offset、Table、Middle Air及Upper Air字段的所有位
  • PTRS_PER_PTE,PTRS_PER_PMD,PTRS_PER_PUS以及PTRS_PER_PGD:用于計算頁表、頁中間目錄、頁上級目錄和頁全局目錄表中表現的個數。

頁表處理

pte_t、pmd_t、pud_t和pgd_t分別描述頁表項、頁中間目錄項、頁上級目錄項和頁全局目錄項。

五個類型轉換宏(__pte,__pmd,__pgd和__pgprot)把一個無符號整數轉換成所需的類型。另外的五個類型轉換宏(pte_val,pmd_val,pud_val,pgd_val和pgport_val)執行相反的轉換。

如果相應的表項值位0,那么,宏pte_none、pmd_none、pud_none、和pgd_none產生的值為1,否則產生的值為0

將虛擬地址轉換物理地址

進程訪問的都是用戶空間內虛擬地址,內核訪問的都是內核虛擬地址,進程用不了內核虛擬地址,同樣內核用不了進程內虛擬地址。如果我們將用戶空間地址傳給內核,內核必須先將找到該用戶空間虛擬地址對應的物理地址,再將物理地址轉換成內核虛擬地址,內核才能訪問數據

代碼地址:http://edsionte.com/techblog/archives/1966

static void get_pgtable_macro(void)
{printk("PAGE_OFFSET = 0x%lx\n", PAGE_OFFSET);printk("PGDIR_SHIFT = %d\n", PGDIR_SHIFT);printk("PUD_SHIFT = %d\n", PUD_SHIFT);printk("PMD_SHIFT = %d\n", PMD_SHIFT);printk("PAGE_SHIFT = %d\n", PAGE_SHIFT);printk("PTRS_PER_PGD = %d\n", PTRS_PER_PGD);printk("PTRS_PER_PUD = %d\n", PTRS_PER_PUD);printk("PTRS_PER_PMD = %d\n", PTRS_PER_PMD);printk("PTRS_PER_PTE = %d\n", PTRS_PER_PTE);printk("PAGE_MASK = 0x%lx\n", PAGE_MASK);
}
static unsigned long vaddr2paddr(unsigned long vaddr)
{pgd_t *pgd;pud_t *pud;pmd_t *pmd;pte_t *pte;unsigned long paddr = 0;unsigned long page_addr = 0;unsigned long page_offset = 0;pgd = pgd_offset(current->mm, vaddr);printk("pgd_val = 0x%lx\n", pgd_val(*pgd));printk("pgd_index = %lu\n", pgd_index(vaddr));if (pgd_none(*pgd)) {printk("not mapped in pgd\n");return -1;}pud = pud_offset(pgd, vaddr);printk("pud_val = 0x%lx\n", pud_val(*pud));if (pud_none(*pud)) {printk("not mapped in pud\n");return -1;}pmd = pmd_offset(pud, vaddr);printk("pmd_val = 0x%lx\n", pmd_val(*pmd));printk("pmd_index = %lu\n", pmd_index(vaddr));if (pmd_none(*pmd)) {printk("not mapped in pmd\n");return -1;}pte = pte_offset_kernel(pmd, vaddr);printk("pte_val = 0x%lx\n", pte_val(*pte));printk("pte_index = %lu\n", pte_index(vaddr));if (pte_none(*pte)) {printk("not mapped in pte\n");return -1;}//頁框物理地址機制 | 偏移量page_addr = pte_val(*pte) & PAGE_MASK;page_offset = vaddr & ~PAGE_MASK;paddr = page_addr | page_offset;printk("page_addr = %lx, page_offset = %lx\n", page_addr, page_offset);printk("vaddr = %lx, paddr = %lx\n", vaddr, paddr);return paddr;
}
static int __init v2p_init(void)
{unsigned long vaddr = 0;printk("vaddr to paddr module is running..\n");get_pgtable_macro();printk("\n");vaddr = (unsigned long)vmalloc(1000 * sizeof(char));if (vaddr == 0) {printk("vmalloc failed..\n");return 0;}printk("vmalloc_vaddr=0x%lx\n", vaddr);vaddr2paddr(vaddr);printk("\n\n");vaddr = __get_free_page(GFP_KERNEL);if (vaddr == 0) {printk("__get_free_page failed..\n");return 0;}printk("get_page_vaddr=0x%lx\n", vaddr);vaddr2paddr(vaddr);return 0;
}
static void __exit v2p_exit(void)
{printk("vaddr to paddr module is leaving..\n");vfree((void *)vaddr);free_page(vaddr);
}

整個程序的結構如下:

  1. get_pgtable_macro()打印當前系統分頁機制中的一些宏。

  2. 通過vmalloc()在內核空間中分配內存,調用vaddr2paddr()將虛擬地址轉化成物理地址。

  3. 通過__get_free_pages()在內核空間中分配頁框,調用vaddr2paddr()將虛擬地址轉化成物理地址。

  4. 分別通過vfree()和free_page()釋放申請的內存空間。

vaddr2paddr()的執行過程如下:

  1. 通過pgd_offset計算頁全局目錄項的線性地址pgd,傳入的參數為內存描述符mm和線性地址vaddr。接著打印pgd所指的頁全局目錄項。

  2. 通過pud_offset計算頁上級目錄項的線性地址pud,傳入的參數為頁全局目錄項的線性地址pgd和線性地址vaddr。接著打印pud所指的頁上級目錄項。

  3. 通過pmd_offset計算頁中間目錄項的線性地址pmd,傳入的參數為頁上級目錄項的線性地址pud和線性地址vaddr。接著打印pmd所指的頁中間目錄項。

  4. 通過pte_offset_kernel計算頁表項的線性地址pte,傳入的參數為頁中間目錄項的線性地址pmd和線性地址vaddr。接著打印pte所指的頁表項。

  5. pte_val(*pte)先取出頁表項,與PAGE_MASK相與的結果是得到要訪問頁的物理地址;vaddr&~PAGE_MASK用來得到線性地址offset字段;兩者或運算得到最終的物理地址。

  6. 打印物理地址。

我們可以獲得物理地址了,就可以使用__pa(地址)宏可以將物理地址轉換為邏輯地址(內核中的虛擬地址),使用__va(地址)可以做相反的操作

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/379510.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/379510.shtml
英文地址,請注明出處:http://en.pswp.cn/news/379510.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

錄制caf 轉 mp3

編譯需要使用的 lame庫http://www.cocoachina.com/bbs/read.php?tid108237參考的文章http://blog.csdn.net/ysy441088327/article/details/7392842說起來,我一直在找一個音頻轉換成mp3的方法。一年前,我成功編譯出了一個lame for armv7的庫。苦于不會使…

杭電2012-素數判定(C)

Problem Description 對于表達式n^2n41&#xff0c;當n在&#xff08;x,y&#xff09;范圍內取整數值時&#xff08;包括x,y&#xff09;(-39<x<y<50)&#xff0c;判定該表達式的值是否都為素數。 Input 輸入數據有多組&#xff0c;每組占一行&#xff0c;由兩個整數…

math.ceil帶小數點_JavaScript中帶有示例的Math.ceil()方法

math.ceil帶小數點JavaScript | Math.ceil()方法 (JavaScript | Math.ceil() Method) Math.ceil() is a function in math library of JavaScript that is used to round up the number passed to the function. The method will return the nearest integer value indeed is g…

開發記要 詭異的變量

告別繁體文盲,從寫blog開始 Variable命名很重要,有多重要,看看.net和java的加密就知道, 都是把variable改到一塌糊塗,你想看看都沒門. 但是這幾天看遺留系統的代碼,真是大開眼界。 我一直以為別人寫a,b,c,d這些單字節variable已經很過分。直到我看到以下這幾個&#xff0…

排序算法---快速排序、堆排序、冒泡排序

排序算法1 快速排序代碼實現stdlib庫快排2 堆排序堆排序的基本思想如何構造一個大頂堆排序3 冒泡排序1 快速排序 文章原地址&#xff1a;https://blog.csdn.net/morewindows/article/details/6684558 快速排序的平均時間復雜度是0(NlogN)&#xff0c;它采用了一種分治的策略&a…

CSS Hack 匯總快查

*:lang(zh) select {font:12px !important;} /*FF的專用*/ select:empty {font:12px !important;} /*safari可見*/ 這里select是選擇符&#xff0c;根據情況更換。第二句是MAC上safari瀏覽器獨有的。 僅IE7識別 *html {…} 當面臨需要只針對IE7做樣式的時候就可以采用這個HACK…

杭電2013-蟠桃記(C++)

Problem Description 喜歡西游記的同學肯定都知道悟空偷吃蟠桃的故事&#xff0c;你們一定都覺得這猴子太鬧騰了&#xff0c;其實你們是有所不知&#xff1a;悟空是在研究一個數學問題&#xff01; 什么問題&#xff1f;他研究的問題是蟠桃一共有多少個&#xff01; 不過&#…

c#中重載單目運算符-_C#程序重載二進制運算符(-,*,/)

c#中重載單目運算符-Here, we will design overloaded methods for binary operators: minus, multiply and divide. In the below program, we will create a Calculator class with data member val. 在這里&#xff0c;我們將為二進制運算符設計重載方法&#xff1a;減&…

項目總結:華南師范大學校園開發教育android客戶端總結

忽略之前小打小鬧&#xff0c;這個項目算是我的第一個項目--SCNU的網絡公選課的android版本的客戶端。項目是從5月中旬開始的&#xff0c;中間經歷了幾個星期的復習考試時間&#xff0c;到現在可以說是完工了吧&#xff08;或許還有寫細節要修改&#xff09;。這個項目帶給我蠻…

火鳥字幕合并器

火鳥字幕合并器-區塊獨立勾選-保存。漢王 PDF OCR轉載于:https://www.cnblogs.com/hnytwn/archive/2009/10/31/1593395.html

Linux系統編程---守護進程

1 守護進程的概述 Daemon&#xff08;守護進程&#xff09;是運行在后臺的一種特殊進程。它獨立于控制終端并且周期性地執行某種任務或等待處理某些發生的事件。它不需要用戶輸入就能運行而且提供某種服務&#xff0c;不是對整個系統就是對某個用戶程序提供服務。Linux系統的大…

c ++明明的隨機數_從列表C ++程序中隨機建議電影

c 明明的隨機數Problem statement: 問題陳述&#xff1a; Write an application code that will suggest movies from a list randomly and there wont be any repeat while suggesting the movies. That means the same movie wont be suggested twice though it will be don…

郵箱服務器

一&#xff0e;郵箱服務器的基本概念 郵件的客戶端&#xff1a;可以只安裝在電腦上&#xff08;C/S&#xff09;的也可以是網頁形式&#xff08;B/S&#xff09;的 郵件服務器&#xff1a;起到郵件的接受與推送的作用 郵件發送的協議&#xff1a; 協議&#xff1a;就是數據傳輸…

C#提高保存jpg圖像的質量

在程序中直接生成的jpg圖像&#xff0c;漢字有毛邊&#xff0c;經過一番搜索&#xff0c;在msdn上發現了下面控制jpg質量系數的文章&#xff0c;修改后試了一下&#xff0c;效果確實比前面強多了。原理我也不大懂&#xff0c;把代碼貼出來&#xff0c;與大家共享。 聯合圖…

延遲和定時器管理

文章目錄1 內核中時間概念2 標準定時器jiffies和HZ定時器API標準定時器案例3 高精度定時器(HRT)高精度定時器案例4 內核中延遲和睡眠原子上下文非原子上下文1 內核中時間概念 時間概念對計算機來說有些模糊&#xff0c;事實上內核必須在硬件的幫助下才能計算和管理時間。硬件為…

Web開發工具(插件)收集

1.IE Developer Toolbar 瀏覽和修改&#xff0c;選定Web頁上的特定元素&#xff0c;查看HTML對象的類名、ID&#xff0c;以及類似鏈接路徑、tab順序、快捷鍵等。 2.HttpWatch Professional 一款強大的網頁數據分析工具,可以查看當前網頁的http數據 FireFox插件 FireFox下插件實…

cin、cin.get()、cin.getline()、getline()、gets()等函數的用法

轉載&#xff0c;并經過本人補充cin、cin.get()、cin.getline()、getline()、gets()等函數的用法2007/10/27 22:51學C的時候&#xff0c;這幾個輸入函數弄的有點迷糊&#xff1b;這里做個小結&#xff0c;為了自己復習&#xff0c;也希望對后來者能有所幫助&#xff0c;如果有差…

Java StringBuilder subSequence()方法與示例

StringBuilder類subSequence()方法 (StringBuilder Class subSequence() method) subSequence() method is available in java.lang package. subSequence()方法在java.lang包中可用。 subSequence() method is used to return the new set of a character sequence that is a …

Linux設備驅動開發---設備樹的概念

文章目錄1 設備樹機制命名約定別名、標簽和phandleDT編譯器2 表示和尋址設備SPI和I2C尋址平臺設備尋址3 處理資源提取特定應用數據文本字符串單元格和無符號的32位整數布爾提取并分析子節點4 平臺驅動程序與DTOF匹配風格處理非設備樹平臺平臺數據與DT設備樹&#xff08;DT&…

【轉】C#中數組復制的4種方法

C#中數組復制的4種方法 from&#xff1a;http://blog.csdn.net/burningcpu/article/details/1434167今天旁邊的同事MM叫我調了一段程序&#xff0c;她想復制一個數組&#xff0c;int[] pins {9,3,4,9};int [] alias pins;這里出了錯誤&#xff0c;也是錯誤的根源&#xff0c…