ARM32 內核記憶體佈局

来源:https://www.cnblogs.com/linhaostudy/archive/2020/05/09/12857407.html
-Advertisement-
Play Games

Linux內核在啟動時會列印出內核記憶體空間的佈局圖,下麵是ARM Vexpress平臺列印出來的記憶體空間佈局圖: 這部分信息列印是在mem_init()函數中實現的。 編譯器在編譯目標文件並且鏈接完成之後,就可以知道內核映像文件最終的大小,接下來打包成二進位文件,該操作由 控制,其中也劃定了內核的內 ...


Linux內核在啟動時會列印出內核記憶體空間的佈局圖,下麵是ARM Vexpress平臺列印出來的記憶體空間佈局圖:

這部分信息列印是在mem_init()函數中實現的。

[start_kernel->mm_init->mem_init]
	pr_notice("Virtual kernel memory layout:\n"
		  "    vmalloc : 0x%16lx - 0x%16lx   (%6ld GB)\n"
#ifdef CONFIG_SPARSEMEM_VMEMMAP
		  "    vmemmap : 0x%16lx - 0x%16lx   (%6ld GB maximum)\n"
		  "              0x%16lx - 0x%16lx   (%6ld MB actual)\n"
#endif
		  "    fixed   : 0x%16lx - 0x%16lx   (%6ld KB)\n"
		  "    PCI I/O : 0x%16lx - 0x%16lx   (%6ld MB)\n"
		  "    modules : 0x%16lx - 0x%16lx   (%6ld MB)\n"
		  "    memory  : 0x%16lx - 0x%16lx   (%6ld MB)\n"
		  "      .init : 0x%p" " - 0x%p" "   (%6ld KB)\n"
		  "      .text : 0x%p" " - 0x%p" "   (%6ld KB)\n"
		  "      .data : 0x%p" " - 0x%p" "   (%6ld KB)\n",
		  MLG(VMALLOC_START, VMALLOC_END),
#ifdef CONFIG_SPARSEMEM_VMEMMAP
		  MLG((unsigned long)vmemmap,
		      (unsigned long)vmemmap + VMEMMAP_SIZE),
		  MLM((unsigned long)virt_to_page(PAGE_OFFSET),
		      (unsigned long)virt_to_page(high_memory)),
#endif
		  MLK(FIXADDR_START, FIXADDR_TOP),
		  MLM(PCI_IO_START, PCI_IO_END),
		  MLM(MODULES_VADDR, MODULES_END),
		  MLM(PAGE_OFFSET, (unsigned long)high_memory),
		  MLK_ROUNDUP(__init_begin, __init_end),
		  MLK_ROUNDUP(_text, _etext),
		  MLK_ROUNDUP(_sdata, _edata));

編譯器在編譯目標文件並且鏈接完成之後,就可以知道內核映像文件最終的大小,接下來打包成二進位文件,該操作由arch/arm/kernel/vmlinux.ld.S控制,其中也劃定了內核的記憶體佈局。

內核image本身占據的記憶體空間從_text段到 _end段,並且分為如下幾個段:

  • 代碼段:_text和 _etext為代碼段的起始和結束地址,包含了編譯後的內核代碼。
  • init段:__init_begin__init_end為init段的起始和結束地址,包含了大部分的模塊初始化的數據。
  • 數據段:_sdata_edata為數據段的起始和結束地址,包含了大部分內核的變數;
  • BSS段:__bss_start__bss_stop為BSS段的開始和結束地址,包含初始化為0的所有靜態全局變數。

上述幾個段的大小在編譯鏈接時根據內核配置來確定,因為每種配置代碼段和數據段長度都不相同,這取決與要編譯哪些內核模塊,但是起始地址__text總是相同的。內核編譯完成後,會生成一個System.map文件,查詢這個文件可以找到這些地址的具體數值。

內核使用虛擬地址從MODULES_VADDR到MODULES_END這段14MB大小的記憶體區域。

#define MODULES_VADDR		(PAGE_OFFSET - SZ_16M)
#ifdef CONFIG_HIGHMEM
#define MODULES_END		(PAGE_OFFSET - PMD_SIZE)
#else
#define MODULES_END		(PAGE_OFFSET)
#endif

用戶空間和內核空間使用3:1的劃分方法時,內核空間只有1GB大小。這1GB的映射空間,其中有一部分用於直接映射物理地址。這個區域稱為線性映射區。在ARM32平臺上,物理地址[0:760MB]的這一部分記憶體被線性映射到[3GB:3GB+768MB]的虛擬地址上。線性映射區的虛擬地址和物理地址相差PAGE_OFFSET,即3GB。內核中有相關的巨集來實現線性映射區虛擬地址與物理地址的查找過程,例如__pa(x)__va(x)

[arch/arm/include/asm/memory.h]
#define __pa(x)	__virt_to_phys((unsigned long)(x))
#define __va(x) ((void *)__phys_to_virt(phys_addr_t)(x))
static inline phys_addr_t __virt_to_phys(unsigned long x)
{
	return (phys_addr_t)x - PAGE_OFFSET + PHYS_OFFSET;
}

static inline unsigned long __phys_to_virt(phys_addr_t x)
{
	return x - PHYS_OFFSET + PAGE_OFFSET;
}

其中,__pa()把線性映射區的虛擬地址轉換為物理地址,轉換公式很簡單,即用虛擬地址減去PAGE_OFFSET(3GB),然後再加上PHYS_OFFSET(這個值在有的ARM平臺上為0,在ARM Vexpress平臺該值為0x6000_0000)。

那麼高端記憶體的起始地址(760MB)如何確定呢?

在內核初始化記憶體時,在santiy_check_meminfo()函數中確定高端記憶體的起始地址,全局變數high_memory來存放高端記憶體的起始地址。

static void * __initdata vmalloc_min =
	(void *)(VMALLOC_END - (240 << 20) - VMALLOC_OFFSET);
void __init sanity_check_meminfo(void)
{
	phys_addr_t vmalloc_limit = __pa(vmalloc_min - 1) + 1;
	arm_lowmem_limit = vmalloc_limit;
	high_memory = __va(arm_lowmem_limit - 1) + 1;
}

vmalloc_min計算出來的結果是0x2F80_0000,即760MB;

為什麼內核只線性映射760MB呢?剩下的264MB的虛擬地址空間用來做什麼呢?

那是保留給vmallc,fixmap和高端向量等使用的。內核許多驅動使用vmalloc來分配連續的虛擬地址的記憶體,因為有的驅動不需要連續的物理地址的記憶體;除此之外,vmalloc還可以用於高端記憶體的臨時映射。一個32bit系統中實際支持的記憶體數量會超過內核線性映射的長度,但是內核具有對所有記憶體的尋找能力。

/*
 * Just any arbitrary offset to the start of the vmalloc VM area: the
 * current 8MB value just means that there will be a 8MB "hole" after the
 * physical memory until the kernel virtual memory starts.  That means that
 * any out-of-bounds memory accesses will hopefully be caught.
 * The vmalloc() routines leaves a hole of 4kB between each vmalloced
 * area for the same reason. ;)
 */
#define VMALLOC_OFFSET		(8*1024*1024)
#define VMALLOC_START		(((unsigned long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1))
#define VMALLOC_END		0xff000000UL

vmalloc區域在ARM32內核中,從VMALLOC_START開始到VMALLOC_END結束,即從0xf000_0000到0xff00_0000,大小為240MB。從VMALLOC_START開始之前有一個8MB的洞,用於捕捉越界訪問。

內核通常把物理記憶體低於760MB的稱為線性映射記憶體(Normal Memory),而高於760MB以上的稱為高端記憶體(High Memory)。由於32位系統的定址能力只有4GB,對於物理記憶體高於760MB而低於4GB的情況,我們可以從保留240MB的虛擬地址划出一部分用於動態映射高端記憶體,這樣內核就可以訪問到全部的4GB的記憶體了。如果物理記憶體高於4GB,那麼在ARMv7-A架構中就要使用LPE機制來擴展物理記憶體訪問了。用於映射高端記憶體的虛擬地址空間有限,所以又可以劃分為兩部分,一部分是臨時映射區,另一部分為固定映射區。PKMAP指向的就是固定映射區。如圖2.6所示是ARM Vexpress平臺上畫出內核空間的記憶體佈局圖,詳細可以參考文檔documentation/arm/memory.txt文件。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • tune2fs命令允許系統管理員在Linux ext2、ext3或ext4文件系統上調整、設置、查看文件系統參數。tune2fs -l 只會顯示 superblock 上的內容。有時候使用tune2fs命令遇到類似“Couldn't find valid filesystem superblock”... ...
  • 1,目標及展示 首先希望實現文字、圖片、控制項等在觸發後,呈現飄散並消失的效果。在QT常式《Qt Quick Particles Examples》是一個海星點擊滑鼠後呈現打散的效果,這個效果和最終需要的略有不同,所以我們在它的基礎上再加上我需要的一些元素,最終實現如下效果。 圖1(gif) 圖6 2 ...
  • 做為一個過來人,我談談我自己的看法,歡迎大家補充: 首先肯定的一點是:不要一上來就看內核代碼,基本上你會很快被挫敗感打敗。內核正在變得越來越龐大,學習曲線越來越陡峭,當你一無所知的時候冒然進入linux kernel,你會發現處處都是障礙,處處都是大坑,你根本走不下去。最好的方法是把對內核源代碼的熱 ...
  • Linux有非常多的版本,比如世面上常見的有 Ubuntu、RedHat、Fedora、Centos等等,這麼多的版本我們究竟該選哪一個呢?今天我帶大家對各個版本進行一下分析和比較,幫助大家來做出更好的選擇。 (一)Linux 是什麼? 首先瞭解一下Linux是什麼。它是一套類UNIX的操作系統,最 ...
  • 步驟 0 uboot 將 zImage 複製到記憶體之後,跳轉到 zImage 處開始執行,首先執行的代碼是 arch/arm/boot/compressed/head.S 文件,首先是一些涉及不同體繫結構調試相關的彙編巨集定義 ifdef DEBUG if defined(CONFIG_DEBUG_I ...
  • 大家好,我是良許。 不管你用的是什麼操作系統,網速都是你非常關心的一個性能指標,畢竟,誰都不想看個視頻結果網速卡到你懷疑人生。本文介紹三個 Linux 命令行下的網路測速工具,讓你隨時隨地知道你的網路狀況。 fast 是 Netflix 提供的一項服務,它不僅可以通過命令行來使用,而且可以直接在 W ...
  • Linux常用命令大全(非常全!!!) 最近都在和Linux打交道,感覺還不錯。我覺得Linux相比windows比較麻煩的就是很多東西都要用命令來控制,當然,這也是很多人喜歡linux的原因,比較短小但卻功能強大。我將我瞭解到的命令列舉一下,僅供大家參考: 系統信息 arch 顯示機器的處理器架構 ...
  • linux進程,這塊太難了,太多命令,太多新概念.作為初學者戰戰兢兢.同時也在匍匐前進. ...
一周排行
    -Advertisement-
    Play Games
  • .Net8.0 Blazor Hybird 桌面端 (WPF/Winform) 實測可以完整運行在 win7sp1/win10/win11. 如果用其他工具打包,還可以運行在mac/linux下, 傳送門BlazorHybrid 發佈為無依賴包方式 安裝 WebView2Runtime 1.57 M ...
  • 目錄前言PostgreSql安裝測試額外Nuget安裝Person.cs模擬運行Navicate連postgresql解決方案Garnet為什麼要選擇Garnet而不是RedisRedis不再開源Windows版的Redis是由微軟維護的Windows Redis版本老舊,後續可能不再更新Garne ...
  • C#TMS系統代碼-聯表報表學習 領導被裁了之後很快就有人上任了,幾乎是無縫銜接,很難讓我不想到這早就決定好了。我的職責沒有任何變化。感受下來這個系統封裝程度很高,我只要會調用方法就行。這個系統交付之後不會有太多問題,更多應該是做小需求,有大的開發任務應該也是第二期的事,嗯?怎麼感覺我變成運維了?而 ...
  • 我在隨筆《EAV模型(實體-屬性-值)的設計和低代碼的處理方案(1)》中介紹了一些基本的EAV模型設計知識和基於Winform場景下低代碼(或者說無代碼)的一些實現思路,在本篇隨筆中,我們來分析一下這種針對通用業務,且只需定義就能構建業務模塊存儲和界面的解決方案,其中的數據查詢處理的操作。 ...
  • 對某個遠程伺服器啟用和設置NTP服務(Windows系統) 打開註冊表 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NtpServer 將 Enabled 的值設置為 1,這將啟用NTP伺服器功 ...
  • title: Django信號與擴展:深入理解與實踐 date: 2024/5/15 22:40:52 updated: 2024/5/15 22:40:52 categories: 後端開發 tags: Django 信號 松耦合 觀察者 擴展 安全 性能 第一部分:Django信號基礎 Djan ...
  • 使用xadmin2遇到的問題&解決 環境配置: 使用的模塊版本: 關聯的包 Django 3.2.15 mysqlclient 2.2.4 xadmin 2.0.1 django-crispy-forms >= 1.6.0 django-import-export >= 0.5.1 django-r ...
  • 今天我打算整點兒不一樣的內容,通過之前學習的TransformerMap和LazyMap鏈,想搞點不一樣的,所以我關註了另外一條鏈DefaultedMap鏈,主要調用鏈為: 調用鏈詳細描述: ObjectInputStream.readObject() DefaultedMap.readObject ...
  • 後端應用級開發者該如何擁抱 AI GC?就是在這樣的一個大的浪潮下,我們的傳統的應用級開發者。我們該如何選擇職業或者是如何去快速轉型,跟上這樣的一個行業的一個浪潮? 0 AI金字塔模型 越往上它的整個難度就是職業機會也好,或者說是整個的這個運作也好,它的難度會越大,然後越往下機會就會越多,所以這是一 ...
  • @Autowired是Spring框架提供的註解,@Resource是Java EE 5規範提供的註解。 @Autowired預設按照類型自動裝配,而@Resource預設按照名稱自動裝配。 @Autowired支持@Qualifier註解來指定裝配哪一個具有相同類型的bean,而@Resourc... ...