麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁 > 學院 > 開發設計 > 正文

Linux內存管理:ARM Memory Layout以及mmu配置

2019-11-09 19:04:06
字體:
來源:轉載
供稿:網友

在內核進行page初始化以及mmu配置之前,首先需要知道整個memory map。

1. ARM Memory Layout

這里寫圖片描述

PAGE_OFFSET Start address of Kernel space 0xC000_0000

lowmem Kernel direct-mapped RAM region (1:1 mapping) Maximum 896M

HIGH_MEMORY End address of lowmem PAGE_OFFSET + MEMORY_SIZE

pkmap 用來把HIGHMEM page 永久映射到 kernel space 2MB (這個大小每個平臺不一樣) kmap() / kunmap()

Page gap To against out-of-bounds errors 8MB

vmalloc vmalloc() / ioremap() space

DMA DMA memory mapping region

Fixmap kmap()可能會進入睡眠,所以不能用在中斷上下文等地方. 所以Fixmap就是用于在中斷上下文中把 highmem映射到內核空間的. Mapping HIGHMEM pages atomically kmap_atomic() :Fixmap在使用這個函數,所以可以在中斷上下文中使用

Vector CPU vectors are mapped here

Modules Kernel modules inserted via insmod are placed here 16MB (14MB, if HIGHMEM is enabled)

在內核初始化的時候,上面說的lowmemory中,還需要去除一些reserved memory。這些預留的內存是供一些外設使用的。下面來看一下預留內存的去除方式以及內核怎么讀取預留的。 (這里不包含具體的內存分配內容,比如slab或者buddy系統等)。

2. 在bootloader判斷物理內核地址范圍之后,會修改相應的device tree節點。

以高通平臺為例,bootloader中有如下函數會負責更新device tree中的memory node

int update_device_tree() { ... ret = fdt_path_offset(fdt, "/memory"); offset = ret; ret = target_dev_tree_mem(fdt, offset); ...}

“/memory”一般定義在sekeleton.dtsi,這也是為什么雖然skeleton.dtsi文件里邊都是空的內容,但還是需要include這個文件的原因。

//skeleton64.dtsi/ { #address-cells = <2>; #size-cells = <2>; cpus { }; soc { }; chosen { }; aliases { }; memory { device_type = "memory"; reg = <0 0 0 0>; };};

然后在kernel里調用如下函數來讀取memory大小等賦值給memblock變量:

setup_machine_fdt(){ ... of_scan_flat_dt(early_init_dt_scan_memory, NULL); ...}int __init early_init_dt_scan_memory(unsigned long node, const char *uname, int depth, void *data){ const char *type = of_get_flat_dt_linux,usable-memory", &l); if (reg == NULL) reg = of_get_flat_dt_prop(node, "reg", &l); if (reg == NULL) return 0; endp = reg + (l / sizeof(__be32)); pr_debug("memory scan node %s, reg size %d, data: %x %x %x %x,/n", uname, l, reg[0], reg[1], reg[2], reg[3]); while ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) { u64 base, size; base = dt_mem_next_cell(dt_root_addr_cells, &reg); size = dt_mem_next_cell(dt_root_size_cells, &reg); if (size == 0) continue; pr_debug(" - %llx , %llx/n", (unsigned long long)base, (unsigned long long)size); early_init_dt_add_memory_arch(base, size); } return 0;}

3. 內核讀到device tree節點之后,會把所有的內存范圍保留在memblock里邊。然后去掉所有預先保留的內存(比如高通msm平臺預留給modem的內存等)。把內核分成lowmemory和highmemory等

在內核啟動之后,

start_kernel()->setup_arch()->setup_arch()->sanity_check_meminfo()

的時候打印的memblock的內容為:

<6>[0.000000] [0:wapper:0] sanity_check_meminfo memblock.memory.cnt=2<6>[0.000000] [0:wapper:0] pys_addr vmalloc_limit = 0xa9c00000<6>[0.000000] [0:wapper:0] count = 1 , reg->base =0x80000000 , reg->size =0x2fd00000<6>[0.000000] [0:wapper:0] count = 2 , reg->base =0xb0000000 , reg->size =0x30000000<6>[0.000000] [0:wapper:0] arm_lowmem_limit =0xa9c00000

內存分為兩個CS: CS1基地址為0x80000000,大小為0x30000000。 CS2基地址為0xb0000000,大小為0x30000000。 所以物理內存開始地址為0x800000000,總的大小為1.5GB。 但中間缺了0x2fd00000到0x30000000的3MB大小的內存,哪里去了??(應該是bootloader改的~~,預留了sec_debug相關的內存)

這段3MB里邊,包含了sec_dbg的內容,但大小沒有3MB這么大,其余的用作什么了還得查<0>[0.000000] [0:swapper:0] sec_dbg_setup: str=@0xaff00008<0>[0.000000] [0:swapper:0] sec_dbg_setup: secdbg_paddr = 0xaff00008<0>[0.000000] [0:swapper:0] sec_dbg_setup: secdbg_size = 0x80000

之后會調用如下函數,讀取memory相關的device tree內容,預留modem,audio等相關的內存:

setup_arch()->arm_memblock_init()->dma_contiguous_reserve()->dma_contiguous_early_removal_fixup()

這時打印的內容為:

<6>[0.000000] [0:swapper:0] arm_lowmem_limit =0xa9c00000<6>[0.000000] [0:swapper:0] cma: Found external_image__region@0, memory base 0x85500000, size 19 MiB, limit 0xffffffff<6>[0.000000] [0:swapper:0] cma: Found modem_adsp_region@0, memory base 0x86800000, size 88 MiB, limit 0xffffffff<6>[0.000000] [0:swapper:0] cma: Found pheripheral_region@0, memory base 0x8c000000, size 6 MiB, limit 0xffffffff<6>[0.000000] [0:swapper:0] cma: Found venus_region@0, memory base 0x8c600000, size 5 MiB, limit 0xffffffff<6>[0.000000] [0:swapper:0] cma: Found secure_region@0, memory base 0x00000000, size 109 MiB, limit 0xffffffff<6>[0.000000] [0:swapper:0] cma: Found qseecom_region@0, memory base 0x00000000, size 13 MiB, limit 0xffffffff<6>[0.000000] [0:swapper:0] cma: Found audio_region@0, memory base 0x00000000, size 3 MiB, limit 0xffffffff<6>[0.000000] [0:swapper:0] cma: Found splash_region@8E000000, memory base 0x8e000000, size 20 MiB, limit 0xffffffff

讀取的dts文件內容可以找到,,內容如下:

{ memory { #address-cells = <2>; #size-cells = <2>;/* Additionally Reserved 6MB for TIMA and Increased the TZ app size * by 2MB [total 8 MB ] */ external_image_mem: external_image__region@0 { linux,reserve-contiguous-region; linux,reserve-region; linux,remove-completely; reg = <0x0 0x85500000 0x0 0x01300000>; label = "external_image_mem"; }; modem_adsp_mem: modem_adsp_region@0 { linux,reserve-contiguous-region; linux,reserve-region; linux,remove-completely; reg = <0x0 0x86800000 0x0 0x05800000>; label = "modem_adsp_mem"; }; peripheral_mem: pheripheral_region@0 { linux,reserve-contiguous-region; linux,reserve-region; linux,remove-completely; reg = <0x0 0x8C000000 0x0 0x0600000>; label = "peripheral_mem"; }; venus_mem: venus_region@0 { linux,reserve-contiguous-region; linux,reserve-region; linux,remove-completely; reg = <0x0 0x8C600000 0x0 0x0500000>; label = "venus_mem"; }; secure_mem: secure_region@0 { linux,reserve-contiguous-region; reg = <0 0 0 0x6D00000>; label = "secure_mem"; }; qseecom_mem: qseecom_region@0 { linux,reserve-contiguous-region; reg = <0 0 0 0xD00000>; label = "qseecom_mem"; }; audio_mem: audio_region@0 { linux,reserve-contiguous-region; reg = <0 0 0 0x314000>; label = "audio_mem"; }; cont_splash_mem: splash_region@8E000000 { linux,reserve-contiguous-region; linux,reserve-region; reg = <0x0 0x8E000000 0x0 0x1400000>; label = "cont_splash_mem"; }; };};

之后在

setup_arch()->arm_memblock_init()->dma_contiguous_reserve()->dma_contiguous_early_removal_fixup()還會調用一次sanity_check_meminfo()函數

這時打印的內容變成了

<6>[0.000000] [0:swapper:0] pys_addr vmalloc_limit = 0xa9c00000<6>[0.000000] [0:swapper:0] count = 1 , reg->base =0x80000000 , reg->size =0x5500000<6>[0.000000] [0:swapper:0] count = 2 , reg->base =0x8cb00000 , reg->size =0x23200000<6>[0.000000] [0:swapper:0] count = 3 , reg->base =0xb0000000 , reg->size =0x30000000<6>[0.000000] [0:swapper:0] arm_lowmem_limit =0xb1200000

比較兩次調用sanity_check_meminfo()函數打印的log,可以看到扣除的內存范圍,這些里邊只有external_image_mem,modem_adsp_mem,peripheral_mem,venus_mem這幾個被扣除了。 后面的secure_region,qseecom_region,audio_region,splash_region哪去了??(這部分被ion memory預留!!)

以下是扣除的內容

external_image_mem: 0x85500000~0x86800000 大小為 19MBmodem_adsp_mem :0x86800000 ~0x8C000000 大小為 88MBperipheral_mem : 0x8C000000 ~ 0x8C600000 大小為6MBvenus_mem:0x8c600000 ~ 0x8cb00000 大小為5MBsecure_mem : 0xd9000000~ 0xe0000000 大小為112MB //這個與上面的109MB相比大小被調整,為什么?qseecom_region : 0xd8000000 ~ 0xd9000000 大小為16MB////這個與上面的109MB相比大小也被調整,為什么?audio_mem : 0xd7c00000 大小為4MB//大小被調整splash_region : 0x8E000000~ 0x8F400000 大小為20MBdefault region :0xa9400000 ~ 0xa9c00000 大小為8MB

external_image_mem,modem_adsp_mem,peripheral_mem,venus_mem這些被扣除前后,memblock的 內容如下:

<6>[0.000000] [0:swapper:0] count = 1 , reg->base =0x80000000 , reg->size =0x2fd00000<6>[0.000000] [0:swapper:0] count = 2 , reg->base =0xb0000000 , reg->size =0x30000000//第一次打印的時候是這樣的,第二次打印就變成下面這樣了<6>[0.000000] [0:swapper:0] count = 1 , reg->base =0x80000000 , reg->size =0x5500000<6>[0.000000] [0:swapper:0] count = 2 , reg->base =0x8cb00000 , reg->size =0x23200000<6>[0.000000] [0:swapper:0] count = 3 , reg->base =0xb0000000 , reg->size =0x30000000<6>[0.000000] [0:swapper:0] arm_lowmem_limit =0xb1200000//vmalloc被cmdline設置為了340MB,所以vmalloc_limit= 0xb1200000//(0xff000000 - 0x15400000(340MB)的值,也就是從0xff00000開始減去vmalloc大小得到的值)。//這個值被調整完之后變成arm_lowmem_limit = 0xa9c00000。 //但第二次被sanity_check_meminfo()函數打印的時候被調整成了0xb1200000,怎么調整的??//arm_lowmem_limit這個是最終劃分Lowmemory和其他vmalloc區域的標準。//從下面的可以看到lowmemory地址最大的區域就是0xf000000~0xf120000。最大地址就到0xf1200000,和arm_lowmem_limit是一樣的。//highmemory的開始地址是high_memory的值,大小如下://high_memory = __va(arm_lowmem_limit - 1) + 1; //這個值加上VMALLOC_OFFSET即為vmalloc的開始地址//#define VMALLOC_START ((unsigned long)high_memory + VMALLOC_OFFSET)//VMALLOC_OFFSET一般為8MB

整個內存的示意圖 這里寫圖片描述

<6>[0.000000] [0:swapper: 0] Memory: 1243908K/1448960K available (10539K kernel code, 1363K rwdata, 4472K rodata, 1417K init, 5844K bss, 205052K reserved, 632832K highmem)<6>[0.000000] [0:swapper: 0] Virtual kernel memory layout:<6>[0.000000] [0:swapper: 0] vector : 0xffff0000 - 0xffff1000 ( 4 kB)<6>[0.000000] [0:swapper: 0] fixmap : 0xfff00000 - 0xfffe0000 ( 896 kB)<6>[0.000000] [0:swapper: 0] arm_lowmem_limit = 0xf1200000 <6>[0.000000] [0:swapper: 0] <6>[0.000000] [0:swapper: 0] start_phys : 0xf0000000 end_phys : 0x20000000 <6>[0.000000] [0:swapper: 0] vmalloc : 0xf1200000 - 0xff000000 ( 222 MB)<6>[0.000000] [0:swapper: 0] lowmem : 0xf0000000 - 0xf1200000 ( 18 MB)<6>[0.000000] [0:swapper: 0] start_phys : 0xccb00000 end_phys : 0xefd00000 <6>[0.000000] [0:swapper: 0] vmalloc : 0xefd00000 - 0xf0000000 ( 3 MB)<6>[0.000000] [0:swapper: 0] lowmem : 0xccb00000 - 0xefd00000 ( 562 MB)<6>[0.000000] [0:swapper: 0] start_phys : 0xc0000000 end_phys : 0xc5500000 <6>[0.000000] [0:swapper: 0] vmalloc : 0xc5500000 - 0xccb00000 ( 118 MB)<6>[0.000000] [0:swapper: 0] lowmem : 0xc0000000 - 0xc5500000 ( 85 MB)<6>[0.000000] [0:swapper: 0] pkmap : 0xbfe00000 - 0xc0000000 ( 2 MB)<6>[0.000000] [0:swapper: 0] modules : 0xbf000000 - 0xbfe00000 ( 14 MB)<6>[0.000000] [0:swapper: 0] .text : 0xc0008000 - 0xc0fa8ec4 (16004 kB)<6>[0.000000] [0:swapper: 0] .init : 0xc1000000 - 0xc1162480 (1418 kB)<6>[0.000000] [0:swapper: 0] .data : 0xc1164000 - 0xc12b8de4 (1364 kB)<6>[0.000000] [0:swapper: 0] .bss : 0xc12c1b3c - 0xc1876b78 (5845 kB)

contig_page_data里邊node_zones的Normal和HighMem的 zone_start_pfn,spanned_pages正好對應上面的地址。

Normal: zone_start_pfn = 0x80000000 zone_start_pfn加上spanned_pages的個數,算一下地址正好是arm_lowmem_limit的值HighMem: zone_start_pfn的值也是正好等于arm_lowmem_limit的值。 zone_start_pfn加上spanned_pages的值也正好等于0xE0000000。

4. 根據上述處理之后,內核得到可用的內存大小以及范圍。然后通過mmu配置等,做內存分頁(paging)。

不管是x86架構還是ARM架構,現在大部分CPU訪問內存,一般通過MMU來實現虛擬內存和物理內存的轉換。 以下是一個簡單的示意圖。(如果要詳細分析的話,要看MMU分幾層,每個page大小怎么配置等等!!參考ARM架構的書) 這里寫圖片描述

在ARM平臺,二級頁表和三級頁表可以選擇用。但目前為止沒有見過三級頁表的,所以略過三級頁表,只看一下二級頁表的。

//在/kernel/arch/arm/include/asm/pgtable.h文件里邊#ifdef CONFIG_ARM_LPAE #include <asm/pgtable-3level.h>#else#include <asm/pgtable-2level.h>#endif

設置一個page大小。這里先略過去寄存器的設置以及page大小類型等。這部分可以參考arm developer’s guide。 先看一下Linux里邊在哪里定義page大小的。

//kernel/include/asm-generic/page.h文件里邊#define PAGE_SHIFT 12#define PAGE_SIZE (1UL << PAGE_SHIFT)//12是最常看到的4k大小的page。

以ARM二級頁表為例,一級頁表和二級頁表的種類有兩種。

//page大小為4K,按下面的組織方式都可以map最大4G的內存地址空間。1. 一級頁表是4096,二級頁表是2562. 一級頁表是2048,二級頁表是512//在ARM Linux中,分別定義了PTRS_PER_PGD,PTRS_PER_PMD,PTRS_PER_PTE分別表示原本三級的頁表,但如果是二級頁表的話。這三個值分別定義為如下:#define PTRS_PER_PTE 512#define PTRS_PER_PMD 1#define PTRS_PER_PGD 2048//上面的值正好對應1級頁表2048,二級頁表512的組織方式。二級頁表中,PUD,PMD沒有用。//一級頁表4096,二級頁表256這樣的配置,就可以定義成如下:#define PTRS_PER_PTE 256#define PTRS_PER_PMD 1#define PTRS_PER_PGD 4096

頁表的示意圖如下:

這里寫圖片描述

create_mapping()函數具體負責頁表的生成。

//create_mapping()有幾個調用路徑1. devicemaps_init()->create_mapping()2. map_lowmem()->create_mapping()3. iotable_init()->create_mapping()4. debug_ll_io_init()->create_mapping()

可以看一下create_mapping()函數怎么按照物理和對應的虛擬內存,構建頁表。

下面舉一個例子看一下某個task訪問某個虛擬地址是怎么一步一步轉成物理地址的。

Linux內核進程,訪問的地址都是內核范圍之內的,只要做一個簡單的偏移就可以在物理地址和虛擬地址之間進行轉換,就不多說了。 用戶進程,其page table的地址,都會保存在其task struct的mm或者active_mm的pgd中。可以根據這個地址,按照頁表的分配方式來算。 這里寫圖片描述

從用戶進程的task_struct中可以知道pgd的地址,當然頁表分配方式上面已經講了,這里是4096,256的分配方式。如果這個進程中,訪問的虛擬地址是0x01206000。按照下面的方式可以算出來是0x578DB000。 這里寫圖片描述

按照ARM Developer’s Guide中的圖,來看一下是怎么一步一步算出來的。

這里寫圖片描述

虛擬地址是:0x01206000Translation table base addre就是pgd的地址(保存在協處理器CP15:C2中),從上面的task_struct->active_mm->pgd可以看到就是0xDD7E3380虛擬地址0x01206000 * 0xFFF000000 ,這個是取虛擬地址前面12bit,然后右移20位,就是0x12,等于18。這個值要乘以4,加上pgd地址。因為第一級頁表有4096個,頁表的每一個項是4個字節,所以就要乘以4。故,要取的地址就是0xDD318048。這個地址里邊的值就是0x53C6381。這個值乘以0xFFFFFF00就是第二級頁表的基地址0x53C6300。取0x01206000虛擬地址的中間8bit,右移8位,然后乘以4,加到上面算出來的二級頁表基地址0x53C6300這個上面去。算出來的值就是0x53C6318。這個地址的值是0x578DBC7F。0x578DBC7F * 0xFFFFF000 加上虛擬地址*0x00000FFF的值,就是0x578DB000。這個就是最終要訪問的物理地址。

用戶進程的內存管理

這里寫圖片描述 1. 進程數據結構: task_struct 2. 進程內存管理數據結構: mm_struct mmap: 進程分配的所有內存的鏈表頭 pgd: page global directory 的地址 3. 進程分配的內存,由vm_area_struct管理 vm_start and vm_end: 虛擬內存的開始地址和結束地址

下圖是用戶進程訪問的虛擬內存通過pgd轉換成物理地址的示意圖,在前面已經詳細講過: 這里寫圖片描述


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 日本爽快片100色毛片视频 | 久久久久久片 | 美女黄页网站免费进入 | 日本在线视频二区 | 久久不射电影 | 九九热九九热 | 亚洲午夜天堂吃瓜在线 | 91麻豆蜜桃一区二区三区 | 欧美 日韩 亚洲 中文 | 亚洲成人福利在线 | 久久久久久久九九九九 | 国产黄色免费网站 | 91九色精品| 久久中文一区 | 牛牛视频在线 | 中文字幕爱爱视频 | 激情久久免费视频 | 亚洲天堂岛国片 | av电影手机在线看 | 99精品国产小情侣高潮露脸在线 | 色阁五月 | 欧美日韩高清在线观看 | 男人的天堂毛片 | 久久99精品久久久久久秒播放器 | 日本黄色免费播放 | 中文字幕在线观看日韩 | 深夜影院a | 日韩毛片免费观看 | 欧产日产国产精品乱噜噜 | 九一免费版在线观看 | 日本aaaa片毛片免费观蜜桃 | 欧美成人久久 | 亚洲成人免费视频在线 | 欧美一级美国一级 | 久久亚洲国产精品 | 天天看天天摸天天操 | 欧美精品亚洲人成在线观看 | 国产精品1区2区在线观看 | 国产中出视频 | 日本精品一区二区 | 国产精品久久久久久久久久iiiii |