0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

《Linux內(nèi)核深度解析》之內(nèi)存地址空間

Linux閱碼場 ? 來源:Linux閱碼場 ? 作者:Linux閱碼場 ? 2022-07-15 14:22 ? 次閱讀

閱碼場用戶程磊對《Linux內(nèi)核深度解析》推薦如下:

1.語言淺顯易懂,內(nèi)容深入淺出。

2.邏輯清晰,條理分明,逐步深入,層層遞進。

3.基于較新的4.12內(nèi)核版本,很多經(jīng)典內(nèi)核書籍雖然寫的都非常好,但是都是基于2.6內(nèi)核,很多在2.6之后引入的新技術(shù)并沒有講到,而本書對這些新技術(shù)都有非常詳細的講解。

3.1 內(nèi)存管理概述

內(nèi)存管理子系統(tǒng)的架構(gòu)如圖 3.1 所示,分為用戶空間、內(nèi)核空間和硬件 3 個層面。

a42ef17c-03f9-11ed-ba43-dac502259ad0.png

圖3.1 內(nèi)存管理架構(gòu)

3.1.1.用戶空間

應(yīng)用程序使用 malloc()申請內(nèi)存,使用 free()釋放內(nèi)存。

malloc()free()glibc 庫的內(nèi)存分配器 ptmalloc 提供的接口,ptmalloc 使用系統(tǒng)調(diào)用brk mmap 向內(nèi)核以頁為單位申請內(nèi)存,然后劃分成小內(nèi)存塊分配給應(yīng)用程序。

用戶空間的內(nèi)存分配器,除了 glibc 庫的 ptmalloc,還有谷歌公司tcmalloc FreeBSDjemalloc。

3.1.2.內(nèi)核空間

1)內(nèi)核空間的基本功能。

虛擬內(nèi)存管理負責(zé)從進程的虛擬地址空間分配虛擬頁,sys_brk 用來擴大或收縮堆,sys_mmap 用來在內(nèi)存映射區(qū)域分配虛擬頁,sys_munmap 用來釋放虛擬頁。

內(nèi)核使用延遲分配物理內(nèi)存的策略,進程第一次訪問虛擬頁的時候,觸發(fā)頁錯誤異常,頁錯誤異常處理程序從頁分配器申請物理頁,在進程的頁表中把虛擬頁映射到物理頁。

頁分配器負責(zé)分配物理頁,當前使用的頁分配器是伙伴分配器。

內(nèi)核空間提供了把頁劃分成小內(nèi)存塊分配的塊分配器,提供分配內(nèi)存的接口 kmalloc()和釋放內(nèi)存的接口 kfree(),支持 3 種塊分配器:SLAB 分配器、SLUB 分配器和 SLOB分配器。

在內(nèi)核初始化的過程中,頁分配器還沒準備好,需要使用臨時的引導(dǎo)內(nèi)存分配器分配內(nèi)存。

2)內(nèi)核空間的擴展功能。

不連續(xù)頁分配器提供了分配內(nèi)存的接口 vmalloc 和釋放內(nèi)存的接口 vfree,在內(nèi)存碎片化的時候,申請連續(xù)物理頁的成功率很低,可以申請不連續(xù)的物理頁,映射到連續(xù)的虛擬頁,即虛擬地址連續(xù)而物理地址不連續(xù)。

處理器內(nèi)存分配器用來為每處理器變量分配內(nèi)存。

連續(xù)內(nèi)存分配器(Contiguous Memory Allocator,CMA)用來給驅(qū)動程序預(yù)留一段連續(xù)的內(nèi)存,當驅(qū)動程序不用的時候,可以給進程使用;當驅(qū)動程序需要使用的時候,把進程占用的內(nèi)存通過回收或遷移的方式讓出來,給驅(qū)動程序使用。

內(nèi)存控制組用來控制進程占用的內(nèi)存資源。

當內(nèi)存碎片化的時候,找不到連續(xù)的物理頁,內(nèi)存碎片整理(“memory compaction的意譯,直譯為“內(nèi)存緊縮”)通過遷移的方式得到連續(xù)的物理頁。

在內(nèi)存不足的時候,頁回收負責(zé)回收物理頁,對于沒有后備存儲設(shè)備支持的匿名頁,把數(shù)據(jù)換出到交換區(qū),然后釋放物理頁;對于有后備存儲設(shè)備支持的文件頁,把數(shù)據(jù)寫回存儲設(shè)備,然后釋放物理頁。如果頁回收失敗,使用最后一招:內(nèi)存耗盡殺手(OOM killer,Out-of-Memory killer),選擇進程殺掉。

3.1.3.硬件層面

處理器包含一個稱為內(nèi)存管理單元(Memory Management Unit,MMU)的部件,負責(zé)把虛擬地址轉(zhuǎn)換成物理地址。

內(nèi)存管理單元包含一個稱為頁表緩存(Translation Lookaside Buffer,TLB)的部件,保存最近使用過的頁表映射,避免每次把虛擬地址轉(zhuǎn)換成物理地址都需要查詢內(nèi)存中的頁表。

為了解決處理器的執(zhí)行速度和內(nèi)存的訪問速度不匹配的問題,在處理器和內(nèi)存之間增加了緩存。緩存通常分為一級緩存和二級緩存,為了支持并行地取指令和取數(shù)據(jù),一級緩存分為數(shù)據(jù)緩存和指令緩存。

3.2 虛擬地址空間布局

3.2.1 虛擬地址空間劃分

因為目前應(yīng)用程序沒有那么大的內(nèi)存需求,所以 ARM64 處理器不支持完全的 64 位虛擬地址,實際支持情況如下。

a487a6fa-03f9-11ed-ba43-dac502259ad0.png

圖3.2 ARM64內(nèi)核/用戶虛擬地址空間劃分

1)虛擬地址的最大寬度是 48 位,如圖 3.2 所示。內(nèi)核虛擬地址在 64 位地址空間的頂部,高 16 位是全 1,范圍是[0xFFFF 0000 000000000xFFFF FFFF FFFF FFFF];用戶虛擬地址在 64 位地址空間的底部,高 16 位是全 0,范圍是[0x0000 0000 0000 0000,0x0000 FFFF FFFF FFFF];高 16 位是全 1 或全 0 的地址稱為規(guī)范的地址,兩者之間是不規(guī)范的地址,不允許使用。

2)如果處理器實現(xiàn)了 ARMv8.2 標準的大虛擬地址(Large Virtual Address,LVA)支持,并且頁長度是 64KB,那么虛擬地址的最大寬度是 52 位。

3)可以為虛擬地址配置比最大寬度小的寬度,并且可以為內(nèi)核虛擬地址和用戶虛擬地址配置不同的寬度。轉(zhuǎn)換控制寄存器(Translation Control RegisterTCR_EL1 的字段 T0SZ 定義了必須是全 0 的最高位的數(shù)量,字段 T1SZ 定義了必須是全 1 的最高位的數(shù)量,用戶虛擬地址的寬度是(64-TCR_EL1.T0SZ),內(nèi)核虛擬地址的寬度是(64-TCR_EL1.T1SZ)。

在編譯 ARM64 架構(gòu)的 Linux 內(nèi)核時,可以選擇虛擬地址寬度。

1)如果選擇頁長度 4KB,默認的虛擬地址寬度是 39 位。

2)如果選擇頁長度 16KB,默認的虛擬地址寬度是 47 位。

3)如果選擇頁長度 64KB,默認的虛擬地址寬度是 42 位。

4)可以選擇 48 位虛擬地址。

ARM64 架構(gòu)的 Linux 內(nèi)核中,內(nèi)核虛擬地址和用戶虛擬地址的寬度相同。

所有進程共享內(nèi)核虛擬地址空間,每個進程有獨立的用戶虛擬地址空間,同一個線程組的用戶線程共享用戶虛擬地址空間,內(nèi)核線程沒有用戶虛擬地址空間。

3.2.2 用戶虛擬地址空間布局

進程的用戶虛擬地址空間的起始地址是 0,長度是 TASK_SIZE,由每種處理器架構(gòu)定義自己的宏 TASK_SIZE。ARM64 架構(gòu)定義的宏 TASK_SIZE 如下所示。

132 位用戶空間程序:TASK_SIZE 的值是TASK_SIZE_32,即0x100000000,等于4GB。

264 位用戶空間程序:TASK_SIZE 的值是 TASK_SIZE_64,即 2VA_BITS字節(jié),VA_BITS是編譯內(nèi)核時選擇的虛擬地址位數(shù)。

arch/arm64/include/asm/memory.h #define VA_BITS (CONFIG_ARM64_VA_BITS) #define TASK_SIZE_64 (UL(1) << VA_BITS) #ifdef CONFIG_COMPAT /* 支持執(zhí)行32位用戶空間程序 */ #define TASK_SIZE_32 UL(0x100000000) /* test_thread_flag(TIF_32BIT)判斷用戶空間程序是不是32位 */ #define TASK_SIZE (test_thread_flag(TIF_32BIT) ?   TASK_SIZE_32 : TASK_SIZE_64) #define TASK_SIZE_OF(tsk) (test_tsk_thread_flag(tsk, TIF_32BIT) ?   TASK_SIZE_32 : TASK_SIZE_64) #else #define TASK_SIZE TASK_SIZE_64 #endif /* CONFIG_COMPAT */

進程的用戶虛擬地址空間包含以下區(qū)域。

1)代碼段、數(shù)據(jù)段和未初始化數(shù)據(jù)段。

2)動態(tài)庫的代碼段、數(shù)據(jù)段和未初始化數(shù)據(jù)段。

3)存放動態(tài)生成的數(shù)據(jù)的堆。

4)存放局部變量和實現(xiàn)函數(shù)調(diào)用的棧。

5)存放在棧底部的環(huán)境變量和參數(shù)字符串。

6)把文件區(qū)間映射到虛擬地址空間的內(nèi)存映射區(qū)域。

內(nèi)核使用內(nèi)存描述符 mm_struct 描述進程的用戶虛擬地址空間,內(nèi)存描述符的主要成員如表 3.1 所示。

a4a0f128-03f9-11ed-ba43-dac502259ad0.png

進程描述符(task_struct)中和內(nèi)存描述符相關(guān)的成員如表 3.2 所示。

a4c3d148-03f9-11ed-ba43-dac502259ad0.png

如果進程不屬于線程組,那么進程描述符和內(nèi)存描述符的關(guān)系如圖 3.3 所示,進程描述符的成員 mm active_mm 都指向同一個內(nèi)存描述符,內(nèi)存描述符的成員 mm_users 1、成員 mm_count 1。

如果兩個進程屬于同一個線程組,那么進程描述符和內(nèi)存描述符的關(guān)系如圖 3.4 所示,每個進程的進程描述符的成員 mm active_mm 都指向同一個內(nèi)存描述符,內(nèi)存描述符的成員 mm_users 2、成員 mm_count 1

a4e044cc-03f9-11ed-ba43-dac502259ad0.png

a503b772-03f9-11ed-ba43-dac502259ad0.png

內(nèi)核線程的進程描述符和內(nèi)存描述符的關(guān)系如圖 3.5 所示,內(nèi)核線程沒有用戶虛擬地址空間,當內(nèi)核線程沒有運行的時候,進程描述符的成員 mm active_mm 都是空指針;當內(nèi)核線程運行的時候,借用上一個進程的內(nèi)存描述符,在被借用進程的用戶虛擬地址空間的上方運行,進程描述符的成員 active_mm 指向借用的內(nèi)存描述符,假設(shè)被借用的內(nèi)存描述符所屬的進程不屬于線程組,那么內(nèi)存描述符的成員 mm_users 不變,仍然是 1,成員mm_count 1 變成 2。

a51c5a20-03f9-11ed-ba43-dac502259ad0.png

為了使緩沖區(qū)溢出攻擊更加困難,內(nèi)核支持為內(nèi)存映射區(qū)域、棧和堆選擇隨機的起始地址。進程是否使用虛擬地址空間隨機化的功能,由以下兩個因素共同決定。

1)進程描述符的成員 personality(個性化)是否設(shè)置 ADDR_NO_RANDOMIZE。

2)全局變量 randomize_va_space0 表示關(guān)閉虛擬地址空間隨機化,1 表示使內(nèi)存映射區(qū)域和棧的起始地址隨機化,2 表示使內(nèi)存映射區(qū)域、棧和堆的起始地址隨機化??梢酝ㄟ^文件“/proc/sys/kernel/randomize_va_space”修改。

mm/memory.c int randomize_va_space __read_mostly = #ifdef CONFIG_COMPAT_BRK  1; #else  2; #endif

為了使舊的應(yīng)用程序(基于 libc5)正常運行,默認打開配置宏 CONFIG_COMPAT_BRK,禁止堆隨機化。所以默認配置是使內(nèi)存映射區(qū)域和棧的起始地址隨機化。

棧通常自頂向下增長,當前只有惠普公司的 PA-RISC 處理器的棧是自底向上增長。棧的起始地址是 STACK_TOP,默認啟用棧隨機化,需要把起始地址減去一個隨機值。STACK_TOP是每種處理器架構(gòu)自定義的宏,ARM64 架構(gòu)定義的 STACK_TOP 如下所示:如果是 64 位用戶空間程序,STACK_TOP 的值是 TASK_SIZE_64;如果是 32 位用戶空間程序,STACK_TOP的值是異常向量的基準地址 0xFFFF0000

arch/arm64/include/asm/processor.h #define STACK_TOP_MAX TASK_SIZE_64 #ifdef CONFIG_COMPAT /* 支持執(zhí)行32位用戶空間程序 */ #define AARCH32_VECTORS_BASE 0xffff0000 #define STACK_TOP (test_thread_flag(TIF_32BIT) ?   AARCH32_VECTORS_BASE : STACK_TOP_MAX) #else #define STACK_TOP STACK_TOP_MAX #endif /* CONFIG_COMPAT */

內(nèi)存映射區(qū)域的起始地址是內(nèi)存描述符的成員 mmap_base。如圖 3.6 所示,用戶虛擬

地址空間有兩種布局,區(qū)別是內(nèi)存映射區(qū)域的起始位置和增長方向不同。

1)傳統(tǒng)布局:內(nèi)存映射區(qū)域自底向上增長,起始地址是 TASK_UNMAPPED_BASE,每種處理器架構(gòu)都要定義這個宏,ARM64 架構(gòu)定義為 TASK_SIZE/4。默認啟用內(nèi)存映射區(qū)域隨機化,需要把起始地址加上一個隨機值。傳統(tǒng)布局的缺點是堆的最大長度受到限制,32 位系統(tǒng)中影響比較大,但是在 64 位系統(tǒng)中這不是問題。

2)新布局:內(nèi)存映射區(qū)域自頂向下增長,起始地址是(STACK_TOP ? 棧的最大長度? 隙)。默認啟用內(nèi)存映射區(qū)域隨機化,需要把起始地址減去一個隨機值。當進程調(diào)用 execve 以裝載 ELF 文件的時候,函數(shù) load_elf_binary 將會創(chuàng)建進程的用戶虛擬地址空間。函數(shù) load_elf_binary 創(chuàng)建用戶虛擬地址空間的過程如圖 3.7 所示。

如果沒有給進程描述符的成員 personality 設(shè)置標志位ADDR_NO_RANDOMIZE(該標志位表示禁止虛擬地址空間隨機化),并且全局變量 randomize_va_space 是非零值,那么給進程設(shè)置標志 PF_RANDOMIZE,允許虛擬地址空間隨機化。

a55156f8-03f9-11ed-ba43-dac502259ad0.png

a5c9532e-03f9-11ed-ba43-dac502259ad0.png

各種處理器架構(gòu)自定義的函數(shù) arch_pick_mmap_layout 負責(zé)選擇內(nèi)存映射區(qū)域的布局。ARM64 架構(gòu)定義的函數(shù) arch_pick_mmap_layout 如下:

arch/arm64/mm/mmap.c1 void arch_pick_mmap_layout(struct mm_struct *mm) 2 { 3 unsigned long random_factor = 0UL; 4 5 if (current->flags & PF_RANDOMIZE) 6 random_factor = arch_mmap_rnd(); 7 8if(mmap_is_legacy()){9 mm->mmap_base = TASK_UNMAPPED_BASE + random_factor; 10 mm->get_unmapped_area = arch_get_unmapped_area; 11 } else { 12 mm->mmap_base = mmap_base(random_factor); 13 mm->get_unmapped_area = arch_get_unmapped_area_topdown; 14 } 15 } 16 17 static int mmap_is_legacy(void) 18 { 19 if (current->personality & ADDR_COMPAT_LAYOUT) 20 return 1; 21 22 if (rlimit(RLIMIT_STACK) == RLIM_INFINITY) 23 return 1; 24 25 return sysctl_legacy_va_layout; 26 }

810 行代碼,如果給進程描述符的成員 personality 設(shè)置標志位 ADDR_COMPAT_LAYOUT 表示使用傳統(tǒng)的虛擬地址空間布局,或者用戶??梢詿o限增長,或者通過文件“/proc/sys/vm/legacy_va_layout”指定,那么使用傳統(tǒng)的自底向上增長的布局,內(nèi)存映射區(qū)域的起始地址是 TASK_UNMAPPED_BASE 加上隨機值,分配未映射區(qū)域的函數(shù)是arch_get_unmapped_area。

1113 行代碼,如果使用自頂向下增長的布局,那么分配未映射區(qū)域的函數(shù)是 arch_get_unmapped_area_topdown,內(nèi)存映射區(qū)域的起始地址的計算方法如下:

arch/arm64/include/asm/elf.h #ifdef CONFIG_COMPAT #define STACK_RND_MASK (test_thread_flag(TIF_32BIT) ?   0x7ff >> (PAGE_SHIFT - 12) :   0x3ffff >> (PAGE_SHIFT - 12)) #else #define STACK_RND_MASK (0x3ffff >> (PAGE_SHIFT - 12)) #endif arch/arm64/mm/mmap.c #define MIN_GAP (SZ_128M + ((STACK_RND_MASK << PAGE_SHIFT) + 1)) #define MAX_GAP (STACK_TOP/6*5) static unsigned long mmap_base(unsigned long rnd) {  unsigned long gap = rlimit(RLIMIT_STACK);  if (gap < MIN_GAP)  gap = MIN_GAP;  else if (gap > MAX_GAP)  gap = MAX_GAP;  return PAGE_ALIGN(STACK_TOP - gap - rnd); }

先計算內(nèi)存映射區(qū)域的起始地址和棧頂?shù)拈g隙:初始值取用戶棧的最大長度,限定不能小于“128MB + 棧的最大隨機偏移值 + 1”,確保用戶棧最大可以達到 128MB;限定不能超過 STACK_TOP 5/6。內(nèi)存映射區(qū)域的起始地址等于“STACK_TOP?間隙?隨機值”,然后向下對齊到頁長度。

回到函數(shù)load_elf_binary:函數(shù) setup_arg_pages 把棧頂設(shè)置為 STACK_TOP 減去隨機120 3.2 虛擬地址空間布局值,然后把環(huán)境變量和參數(shù)從臨時棧移到最終的用戶棧;函數(shù) set_brk 設(shè)置堆的起始地址,如果啟用堆隨機化,把堆的起始地址加上隨機值。

fs/binfmt_elf.c static int load_elf_binary(struct linux_binprm *bprm) {  retval = setup_arg_pages(bprm, randomize_stack_top(STACK_TOP),  executable_stack);  retval = set_brk(elf_bss, elf_brk, bss_prot);  if ((current->flags & PF_RANDOMIZE) && (randomize_va_space > 1)) {  current->mm->brk = current->mm->start_brk = arch_randomize_brk(current->mm);  } }

3.2.3 內(nèi)核地址空間布局

ARM64 處理器架構(gòu)的內(nèi)核地址空間布局如圖 3.8 所示。

a5e0d378-03f9-11ed-ba43-dac502259ad0.png

1)線性映射區(qū)域的范圍是[PAGE_OFFSET,a601e18a-03f9-11ed-ba43-dac502259ad0.png],起始位置是 PAGE_OFFSET =(0xFFFF FFFF FFFF FFFF << (VA_BITS-1)),長度是內(nèi)核虛擬地址空間的一半。稱為線性映射區(qū)域的原因是虛擬地址和物理地址是線性關(guān)系:

虛擬地址 =((物理地址 ? PHYS_OFFSET+ PAGE_OFFSET),其中 PHYS_OFFSET是內(nèi)存的起始物理地址。

2vmemmap 區(qū)域的范圍是 [VMEMMAP_START, PAGE_OFFSET),長度是

VMEMMAP_SIZE =(線性映射區(qū)域的長度 / 頁長度 * page 結(jié)構(gòu)體的長度上限)。

內(nèi)核使用 page 結(jié)構(gòu)體描述一個物理頁,內(nèi)存的所有物理頁對應(yīng)一個 page 結(jié)構(gòu)體數(shù)組。如果內(nèi)存的物理地址空間不連續(xù),存在很多空洞,稱為稀疏內(nèi)存。vmemmap 區(qū)域是稀疏內(nèi)存的 page 結(jié)構(gòu)體數(shù)組的虛擬地址空間。

3PCI I/O 區(qū)域的范圍是[PCI_IO_START, PCI_IO_END),長度是 16MB,結(jié)束地址是PCI_IO_END = (VMEMMAP_START ? 2MB)。

外圍組件互聯(lián)(Peripheral Component Interconnect,PCI)是一種總線標準,PCI I/O 區(qū)域是 PCI 設(shè)備的 I/O 地址空間。

4)固定映射區(qū)域的范圍是[FIXADDR_START, FIXADDR_TOP),長度是FIXADDR_SIZE,結(jié)束地址是 FIXADDR_TOP = (PCI_IO_START ? 2MB)。

固定地址是編譯時的特殊虛擬地址,編譯的時候是一個常量,在內(nèi)核初始化的時候映射到物理地址。

5vmalloc 區(qū)域的范圍是[VMALLOC_START, VMALLOC_END),起始地址是VMALLOC_START,等于內(nèi)核模塊區(qū)域的結(jié)束地址,結(jié)束地址是 VMALLOC_END = (PAGE_OFFSET ?PUD_SIZE ? VMEMMAP_SIZE ? 64KB),其中 PUD_SIZE 是頁上級目錄表項映射的地址空間的長度。

vmalloc 區(qū)域是函數(shù) vmalloc 使用的虛擬地址空間,內(nèi)核使用 vmalloc 分配虛擬地址連續(xù)但物理地址不連續(xù)的內(nèi)存。內(nèi)核鏡像在 vmalloc 區(qū)域,起始虛擬地址是(KIMAGE_VADDR + TEXT_OFFSET) ,其中 KIMAGE_VADDR 是內(nèi)核鏡像的虛擬地址的基準值,等于內(nèi)核模塊區(qū)域的結(jié)束地址MODULES_END;TEXT_OFFSET 是內(nèi)存中的內(nèi)核鏡像相對內(nèi)存起始位置的偏移。

6)內(nèi)核模塊區(qū)域的范圍是[MODULES_VADDR, MODULES_END),長度是 128MB,起始地址是 MODULES_VADDR =(內(nèi)核虛擬地址空間的起始地址 + KASAN 影子區(qū)域的長度)。

內(nèi)核模塊區(qū)域是內(nèi)核模塊使用的虛擬地址空間。

7KASAN 影子區(qū)域的起始地址是內(nèi)核虛擬地址空間的起始地址,長度是內(nèi)核虛擬地址空間長度的 1/8。

內(nèi)核地址消毒劑(Kernel Address SANitizer,KASAN)是一個動態(tài)的內(nèi)存錯誤檢查工具。它為發(fā)現(xiàn)釋放后使用和越界訪問這兩類缺陷提供了快速和綜合的解決方案。

3.3 物理地址空間

物理地址是處理器在系統(tǒng)總線上看到的地址。使用精簡指令集(Reduced Instruction SetComputer,RISC)的處理器通常只實現(xiàn)一個物理地址空間,外圍設(shè)備和物理內(nèi)存使用統(tǒng)一的物理地址空間。有些處理器架構(gòu)把分配給外圍設(shè)備的物理地址區(qū)域稱為設(shè)備內(nèi)存。

處理器通過外圍設(shè)備控制器寄存器訪問外圍設(shè)備,寄存器分為控制寄存器、狀態(tài)寄存器和數(shù)據(jù)寄存器三大類,外圍設(shè)備的寄存器通常被連續(xù)地編址。處理器對外圍設(shè)備寄存器的編址方式有兩種。

1I/O 映射方式(I/O-mapped):英特爾x86 處理器為外圍設(shè)備專門實現(xiàn)了一個單獨的地址空間,稱為“I/O 地址空間”或“I/O 端口空間”,處理器通過專門的 I/O 指令(如x86 in out 指令)來訪問這一空間中的地址單元。

2)內(nèi)存映射方式(memory-mapped):使用精簡指令集的處理器通常只實現(xiàn)一個物理地址空間,外圍設(shè)備和物理內(nèi)存使用統(tǒng)一的物理地址空間,處理器可以像訪問一個內(nèi)存單元那樣訪問外圍設(shè)備,不需要提供專門的 I/O 指令。

程序只能通過虛擬地址訪問外設(shè)寄存器,內(nèi)核提供了以下函數(shù)來把外設(shè)寄存器的物理地址映射到虛擬地址空間。

1)函數(shù) ioremap()把外設(shè)寄存器的物理地址映射到內(nèi)核虛擬地址空間。

void * ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags);

2)函數(shù) io_remap_pfn_range()把外設(shè)寄存器的物理地址映射到進程的用戶虛擬地址空間。

int io_remap_pfn_range(struct vm_area_struct *vma, unsigned long addr,unsigned long pfn, unsigned long size, pgprot_t prot);

除了 SPARC 處理器以外,在其他處理器架構(gòu)中函數(shù) io_remap_pfn_range()和函數(shù) remap_pfn_range()等價。函數(shù) remap_pfn_range()用于把內(nèi)存的物理頁映射到進程的用戶虛擬地址空間。

內(nèi)核提供了函數(shù) iounmap(),它用來刪除函數(shù) ioremap()創(chuàng)建的映射。

void iounmap(void *addr);

ARM64 架構(gòu)的實現(xiàn)

ARM64 架構(gòu)定義了兩種內(nèi)存類型。

1)正常內(nèi)存(Normal Memory):包括物理內(nèi)存和只讀存儲器(ROM)。

2)設(shè)備內(nèi)存(Device Memory):指分配給外圍設(shè)備寄存器的物理地址區(qū)域。

對于正常內(nèi)存,可以設(shè)置共享屬性和緩存屬性。共享屬性用來定義一個位置是否可以被多個核共享,分為不可共享、內(nèi)部共享和外部共享。不可共享是指只被處理器的一個核使用,內(nèi)部共享是指一個處理器的所有核共享或者多個處理器共享,外部共享是指處理器和其他觀察者(比如圖形處理單元或 DMA 控制器)共享。緩存屬性用來定義訪問時是否通過處理器的緩存。

設(shè)備內(nèi)存的共享屬性總是外部共享,緩存屬性總是不可緩存(即必須繞過處理器的緩存)。

ARM64 架構(gòu)根據(jù) 3 種屬性把設(shè)備內(nèi)存分為 4 種類型。

1Device-nGnRnE,這種類型限制最嚴格。

2Device-nGnRE

3Device-nGRE

4Device-GRE,這種類型限制最少。

3 種屬性分別如下。

1)聚集屬性:G 表示聚集(Gathering),nG 表示不聚集(non Gathering)。聚集屬性決定對內(nèi)存區(qū)域的多個訪問是否可以被合并為一個總線事務(wù)。如果地址被標記為“不聚集”,那么必須按照程序里面的地址和長度訪問。如果地址被標記為“聚集”,處理器可以把兩個“寫一個字節(jié)”的訪問合并成一個“寫兩個字節(jié)”的訪問,可以把對相同內(nèi)存位置的多個訪問合并,例如讀相同位置兩次,處理器只需要讀一次,為兩條指令返回相同的結(jié)果。

2)重排序?qū)傩裕?/span>R 表示重排序(Re-ordering),nR 表示不重排序(non Re-ordering)。這個屬性決定對相同設(shè)備的多個訪問是否可以重新排序。如果地址被標記為“不重排序”,那么對同一個塊的訪問總是按照程序順序執(zhí)行。

3)早期寫確認屬性:E 表示早期寫確認(Early Write Acknowledgement),nE 表示不執(zhí)行早期寫確認(non Early Write Acknowledgement)。

這個屬性決定是否允許處理器和從屬設(shè)備之間的中間寫緩沖區(qū)發(fā)送“寫完成”確認。如果地址被標記為“不執(zhí)行早期寫確認”,那么必須由外圍設(shè)備發(fā)送“寫完成”確認。如果地址被標記為“早期寫確認”,那么允許寫緩沖區(qū)在外圍設(shè)備收到數(shù)據(jù)之前發(fā)送“寫完成”確認。

物理地址寬度

目前 ARM64 處理器支持的最大物理地址寬度是 48 位,如果實現(xiàn)了 ARMv8.2 標準的大物理地址(Large Physical Address,LPA)支持,并且頁長度是 64KB,那么物理地址的最大寬度是 52 位。

可以使用寄存器 TCR_EL1Translation Control Register for Exception Level 1,異常級別 1 轉(zhuǎn)換控制寄存器)的字段 IPSIntermediate Physical Address Size,中間物理地址長度)控制物理地址的寬度,IPS 字段的長度是 3 位,IPS 字段的值和物理地址寬度的對應(yīng)關(guān)系如表 3.3 所示。

a617bf32-03f9-11ed-ba43-dac502259ad0.png

審核編輯 :李倩


聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • Linux
    +關(guān)注

    關(guān)注

    87

    文章

    11161

    瀏覽量

    208462
  • 分配器
    +關(guān)注

    關(guān)注

    0

    文章

    193

    瀏覽量

    25664
  • 內(nèi)存管理
    +關(guān)注

    關(guān)注

    0

    文章

    168

    瀏覽量

    14106

原文標題:《Linux內(nèi)核深度解析》選載之內(nèi)存地址空間

文章出處:【微信號:LinuxDev,微信公眾號:Linux閱碼場】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    labview如何獲取到圖像的內(nèi)存地址

    請問各位大佬們,labview如何獲取到圖像的內(nèi)存地址,以便的別的語言中根據(jù)這個內(nèi)存地址中的圖片進行處理?
    發(fā)表于 10-04 18:59

    linux驅(qū)動程序運行在什么空間

    空間擁有對硬件的直接訪問權(quán)限,而用戶空間則受到限制,以保護系統(tǒng)的穩(wěn)定性和安全性。 1. Linux 內(nèi)核和驅(qū)動程序概述 Linux
    的頭像 發(fā)表于 08-30 14:37 ?230次閱讀

    微軟發(fā)布Linux內(nèi)核Rust模塊優(yōu)化補丁

    在此之前,Linux 內(nèi)核中要想實現(xiàn)模塊初始化,必須先創(chuàng)建一個實例,再將其移至特定內(nèi)存空間。然而,經(jīng)過新補丁調(diào)整后,各模塊可直接在預(yù)設(shè)定好的內(nèi)存地址上完成初始化工作。
    的頭像 發(fā)表于 04-02 15:11 ?399次閱讀

    如何在ModusToolbox?中檢查和設(shè)置應(yīng)用程序的內(nèi)存地址?

    如何在ModusToolbox?中檢查和設(shè)置應(yīng)用程序的內(nèi)存地址
    發(fā)表于 03-01 10:16

    Linux內(nèi)核內(nèi)存管理之內(nèi)核非連續(xù)物理內(nèi)存分配

    的主要優(yōu)點是避免了外部碎片,而缺點是需要修改內(nèi)核頁表。顯然,非連續(xù)內(nèi)存區(qū)域的大小必須是4096的倍數(shù)。Linux使用非連續(xù)物理內(nèi)存區(qū)的場景有幾種:(1)為swap區(qū)分配數(shù)據(jù)結(jié)構(gòu);(2)
    的頭像 發(fā)表于 02-23 09:44 ?802次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>內(nèi)核</b><b class='flag-5'>內(nèi)存</b>管理<b class='flag-5'>之內(nèi)核</b>非連續(xù)物理<b class='flag-5'>內(nèi)存</b>分配

    Linux內(nèi)核內(nèi)存管理之ZONE內(nèi)存分配器

    內(nèi)核中使用ZONE分配器滿足內(nèi)存分配請求。該分配器必須具有足夠的空閑頁幀,以便滿足各種內(nèi)存大小請求。
    的頭像 發(fā)表于 02-21 09:29 ?815次閱讀

    Linux內(nèi)核內(nèi)存管理架構(gòu)解析

    內(nèi)存管理子系統(tǒng)可能是linux內(nèi)核中最為復(fù)雜的一個子系統(tǒng),其支持的功能需求眾多,如頁面映射、頁面分配、頁面回收、頁面交換、冷熱頁面、緊急頁面、頁面碎片管理、頁面緩存、頁面統(tǒng)計等,而且對性能也有很高
    的頭像 發(fā)表于 01-04 09:24 ?589次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>內(nèi)核</b><b class='flag-5'>內(nèi)存</b>管理架構(gòu)<b class='flag-5'>解析</b>

    Linux進程地址空間詳解

    RAM 的某些部分永久地分配給內(nèi)核, 并用來存放內(nèi)核代碼以及靜態(tài)內(nèi)核數(shù)據(jù)結(jié)構(gòu). RAM 的其余部分稱為動態(tài)內(nèi)存 (dynamic memory). 動態(tài)
    的頭像 發(fā)表于 12-18 09:45 ?614次閱讀
    <b class='flag-5'>Linux</b>進程<b class='flag-5'>地址</b><b class='flag-5'>空間</b>詳解

    MMU虛擬地址空間布局

    當然虛擬地址空間劃分不只是如此。因為目前應(yīng)用程序沒有那么大的內(nèi)存需求,所以ARM64處理器不支持完全的64位虛擬地址,實際支持情況如下。 (1)-虛擬
    的頭像 發(fā)表于 11-26 16:35 ?794次閱讀

    linux系統(tǒng)查看物理地址

    Linux系統(tǒng)中,訪問和查看物理地址是一個非常重要的任務(wù),因為它提供了對硬件設(shè)備的直接訪問。本文將詳細介紹如何在Linux系統(tǒng)中查看物理地址,包括不同的方法和工具,以及如何解釋和使用
    的頭像 發(fā)表于 11-16 16:47 ?3085次閱讀

    Linux內(nèi)核slab性能優(yōu)化的核心思想

    今天分享一篇內(nèi)存性能優(yōu)化的文章,文章用了大量精美的圖深入淺出地分析了Linux內(nèi)核slab性能優(yōu)化的核心思想,slab是Linux內(nèi)核小對象
    的頭像 發(fā)表于 11-13 11:45 ?546次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>內(nèi)核</b>slab性能優(yōu)化的核心思想

    glibc的內(nèi)存分配回收策略

    Linux內(nèi)存空間簡介 32位Linux平臺下進程虛擬地址空間分布如下圖: 進程虛擬地址
    的頭像 發(fā)表于 11-13 11:16 ?578次閱讀
    glibc的<b class='flag-5'>內(nèi)存</b>分配回收策略

    Linux內(nèi)核內(nèi)存規(guī)整總結(jié)

    1.前言 伙伴系統(tǒng)作為內(nèi)核最基礎(chǔ)的物理頁內(nèi)存分配器,具有高效、實現(xiàn)邏輯簡介等優(yōu)點,其原理頁也盡可能降低內(nèi)存外部碎片產(chǎn)生,但依然無法杜絕碎片問題。外部碎片帶來的最大影響就是內(nèi)存足夠,但是
    的頭像 發(fā)表于 11-11 11:17 ?1206次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>內(nèi)核</b><b class='flag-5'>內(nèi)存</b>規(guī)整總結(jié)

    linux內(nèi)存性能優(yōu)化介紹

    【1】內(nèi)存映射 Linux 內(nèi)核給每個進程都提供了一個獨立且連續(xù)的虛擬地址空間,以便進程可以方便地訪問虛擬
    的頭像 發(fā)表于 11-10 15:23 ?623次閱讀
    <b class='flag-5'>linux</b><b class='flag-5'>內(nèi)存</b>性能優(yōu)化介紹

    Linux 內(nèi)存管理總結(jié)

    一、Linux內(nèi)存管理概述 Linux內(nèi)存管理是指對系統(tǒng)內(nèi)存的分配、釋放、映射、管理、交換、壓縮等一系列操作的管理。在
    的頭像 發(fā)表于 11-10 14:58 ?463次閱讀
    <b class='flag-5'>Linux</b> <b class='flag-5'>內(nèi)存</b>管理總結(jié)