【ARM-Linux开发】Linux内存管理:ARM Memory Layout以及mmu配置
原文:Linux内存管理:ARM
Memory Layout以及mmu配置
在内核进行page初始化以及mmu配置之前,首先需要知道整个memory map。
1. ARM Memory Layout
PAGE_OFFSET
Start address of Kernel space
0xC000_0000lowmem
Kernel direct-mapped RAM region (1:1 mapping)
Maximum 896MHIGH_MEMORY
End address of lowmem
PAGE_OFFSET + MEMORY_SIZEpkmap
用来把HIGHMEM page 永久映射到 kernel space
2MB (这个大小每个平台不一样)
kmap() / kunmap()Page gap
To against out-of-bounds errors
8MBvmalloc
vmalloc() / ioremap() spaceDMA
DMA memory mapping regionFixmap
kmap()可能会进入睡眠,所以不能用在中断上下文等地方.
所以Fixmap就是用于在中断上下文中把 highmem映射到内核空间的.
Mapping HIGHMEM pages atomically
kmap_atomic() :Fixmap在使用这个函数,所以可以在中断上下文中使用Vector
CPU vectors are mapped hereModules
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);
...
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
“/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>; };
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
然后在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_prop(node, "device_type", NULL);
const __be32 *reg, *endp;
int l;
/* We are scanning "memory" nodes only */
if (type == NULL) {
/*
* The longtrail doesn't have a device_type on the
* /memory node, so look for the node called /memory@0.
*/
if (!IS_ENABLED(CONFIG_PPC32) || depth != 1 || strcmp(uname, "memory@0") != 0)
return 0;
} else if (strcmp(type, "memory") != 0)
return 0;
reg = of_get_flat_dt_prop(node, "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, ®);
size = dt_mem_next_cell(dt_root_size_cells, ®);
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;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
3. 内核读到device tree节点之后,会把所有的内存范围保留在memblock里边。然后去掉所有预先保留的内存(比如高通msm平台预留给modem的内存等)。把内核分成lowmemory和highmemory等
在内核启动之后,
start_kernel()->setup_arch()->setup_arch()->sanity_check_meminfo()
- 1
- 1
的时候打印的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
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
内存分为两个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
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
之后会调用如下函数,读取memory相关的device tree内容,预留modem,audio等相关的内存:
setup_arch()->arm_memblock_init()->dma_contiguous_reserve()->dma_contiguous_early_removal_fixup()
- 1
- 1
这时打印的内容为:
<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
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
读取的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";
};
};
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
之后在
setup_arch()->arm_memblock_init()->dma_contiguous_reserve()->dma_contiguous_early_removal_fixup()还会调用一次sanity_check_meminfo()函数
- 1
- 1
这时打印的内容变成了
<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
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
比较两次调用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 大小为 19MB
modem_adsp_mem :0x86800000 ~0x8C000000 大小为 88MB
peripheral_mem : 0x8C000000 ~ 0x8C600000 大小为6MB
venus_mem:0x8c600000 ~ 0x8cb00000 大小为5MB
secure_mem : 0xd9000000~ 0xe0000000 大小为112MB //这个与上面的109MB相比大小被调整,为什么?
qseecom_region : 0xd8000000 ~ 0xd9000000 大小为16MB////这个与上面的109MB相比大小也被调整,为什么?
audio_mem : 0xd7c00000 大小为4MB//大小被调整
splash_region : 0x8E000000~ 0x8F400000 大小为20MB
default region :0xa9400000 ~ 0xa9c00000 大小为8MB
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
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
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
整个内存的示意图
<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)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
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。
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
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
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
设置一个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。
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
以ARM二级页表为例,一级页表和二级页表的种类有两种。
//page大小为4K,按下面的组织方式都可以map最大4G的内存地址空间。
1. 一级页表是4096,二级页表是256
2. 一级页表是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
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
页表的示意图如下:
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()
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
可以看一下create_mapping()函数怎么按照物理和对应的虚拟内存,构建页表。
- Present flag: 表示pte指向的page地址已经存在于内存当中。linux内核中pte_present()函数来检查present flag。
- Accessed flag : Set each time the paging unit addresses the corresponding page frame. This flag may be
used by the operating system when selecting pages to be swapped out. The paging unit
never resets this flag; this must be done by the operating system - Dirty flag : Applies only to the Page Table entries. It is set each time a write operation is performed on
the page frame. As with the Accessed flag, Dirty may be used by the operating system
when selecting pages to be swapped out. The paging unit never resets this flag; this must
be done by the operating system. - Read/Write flag : Contains the access right (Read/Write or Read) of the page or of the Page Table (see the
section “Hardware Protection Scheme” later in this chapter) - User/Supervisor flag : Contains the privilege level required to access the page or Page Table (see the later section
“Hardware Protection Scheme”). - PCD and PWT flags : Controls the way the page or Page Table is handled by the hardware cache (see the section
“Hardware Cache” later in this chapter). - Page Size flag : Applies only to Page Directory entries. If it is set, the entry refers to a 2 MB- or 4 MB-long
page frame (see the following sections). - Global flag : Applies only to Page Table entries. This flag was introduced in the Pentium Pro to prevent
frequently used pages from being flushed from the TLB cache (see the section “Translation
Lookaside Buffers (TLB)” later in this chapter). It works only if the Page Global Enable (PGE)
flag of register cr4 is set.
关于这部分的操作,将在详解函数do_page_fault()函数的时候说明~~
下面举一个例子看一下某个task访问某个虚拟地址是怎么一步一步转成物理地址的。
Linux内核进程,访问的地址都是内核范围之内的,只要做一个简单的偏移就可以在物理地址和虚拟地址之间进行转换,就不多说了。
用户进程,其page table的地址,都会保存在其task struct的mm或者active_mm的pgd中。可以根据这个地址,按照页表的分配方式来算。
从用户进程的task_struct中可以知道pgd的地址,当然页表分配方式上面已经讲了,这里是4096,256的分配方式。如果这个进程中,访问的虚拟地址是0x01206000。按照下面的方式可以算出来是0x578DB000。
按照ARM Developer’s Guide中的图,来看一下是怎么一步一步算出来的。
- 虚拟地址是:0x01206000
- Translation table base addre就是pgd的地址,也就是一级页表的地址(保存在协处理器CP15:C2中),从上面的task_struct->active_mm->pgd可以看到就是0xDD318000
- 虚拟地址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转换成物理地址的示意图,在前面已经详细讲过:
【ARM-Linux开发】Linux内存管理:ARM Memory Layout以及mmu配置的更多相关文章
- ARM裸机开发中内存管理库RT_HEAP的使用
在使用arm芯片进行裸机开发的时候,很多时候都需要内存管理的功能,我们可以使用自己写的内存管理程序,也可以直接使用标准库,不过我一般比较喜欢标准库,速度快,今天就来说说在C语言环境下怎么样进行内存的动 ...
- Linux内核之内存管理
Linux内核之内存管理 Linux利用的是分段+分页单元把逻辑地址转换为物理地址; RAM的某些部分永久地分配给内核, 并用来存放内核代码以及静态内核数据结构; RAM的其余部分称动态内存(dyna ...
- 24小时学通Linux内核之内存管理方式
昨天分析的进程的代码让自己还在头昏目眩,脑子中这几天都是关于Linux内核的,对于自己出现的一些问题我会继续改正,希望和大家好好分享,共同进步.今天将会讲诉Linux如何追踪和管理用户空间进程的可用内 ...
- Linux内核笔记--内存管理之用户态进程内存分配
内核版本:linux-2.6.11 Linux在加载一个可执行程序的时候做了种种复杂的工作,内存分配是其中非常重要的一环,作为一个linux程序员必然会想要知道这个过程到底是怎么样的,内核源码会告诉你 ...
- IOS开发的内存管理
关于IOS开发的内存管理的文章已经很多了,因此系统的知识点就不写了,这里我写点平时工作遇到的疑问以及解答做个总结吧,相信也会有人遇到相同的疑问呢,欢迎学习IOS的朋友请加ios技术交流群:190956 ...
- IOS开发小记-内存管理
关于IOS开发的内存管理的文章已经很多了,因此系统的知识点就不写了,这里我写点平时工作遇到的疑问以及解答做个总结吧,相信也会有人遇到相同的疑问呢,欢迎学习IOS的朋友请加ios技术交流群:190956 ...
- [转载]对iOS开发中内存管理的一点总结与理解
对iOS开发中内存管理的一点总结与理解 做iOS开发也已经有两年的时间,觉得有必要沉下心去整理一些东西了,特别是一些基础的东西,虽然现在有ARC这种东西,但是我一直也没有去用过,个人觉得对内存操作 ...
- 高性能JAVA开发之内存管理
这几天在找一个程序的bug,主要是java虚拟机内存溢出的问题,调研了一些java内存管理的资料,现整理如下: 一.JVM中的对象生命周期 对象的生命周期一般分为7个阶段:创建阶段,应用阶段,不可视阶 ...
- Linux企业级开发技术(6)——libevent企业级开发之内存管理
默认情况下,libevent使用C库的内存管理函数在堆上分配内存.通过提供malloc.realloc和free的替代函数,可以让libevent使用其他的内存管理器.希望libevent使 用一个更 ...
随机推荐
- 怎么在vscode里搜索函数
怎么在vscode里搜索函数?在vsCode编辑器中如何跨文件查找函数的定义? 问题: 比如: 在 a.js中使用 var res = window.unique(arr); 在未知的js文件定义了u ...
- Python sleep()函数用法:线程睡眠
如果需要让当前正在执行的线程暂停一段时间,并进入阻塞状态,则可以通过调用 time 模块的 sleep(secs) 函数来实现.该函数可指定一个 secs 参数,用于指定线程阻塞多少秒. 当前线程调用 ...
- BZOJ 1299: [LLH邀请赛]巧克力棒 【SG函数/博弈分析/高斯消元】
因为太懒,放个博客 我只写了O(2n)O(2^n)O(2n)的 CODE #include <cstdio> int n, x[15]; int main () { for(int T = ...
- 24、自动装配-@Profile环境搭建
24.自动装配-@Profile环境搭建 Spring为我们提供的可以根据当前环境,动态的激活和切换一系列组件的功能. 开发环境.测试环境.正式环境 数据源切换 24.1 添加 数据源和jdbc驱动 ...
- Codeforces Round #470 (rated, Div. 1, based on VK Cup 2018 Round 1) 923D 947D 948E D. Picking Strings
题: OvO http://codeforces.com/contest/947/problem/D 923D 947D 948E 解: 记要改变的串为 P1 ,记目标串为 P2 由变化规则可得: ...
- 001_Python安装
1. 判断电脑是否已经安装python,电脑->运行->输入:python 2. 如果没有安装,去官网下载:https://www.python.org/downloads/ 3. 安装过 ...
- Vue 中的 ref $refs
ref 被用来给DOM元素或子组件注册引用信息.引用信息会根据父组件的 $refs 对象进行注册.如果在普通的DOM元素上使用,引用信息就是元素; 如果用在子组件上,引用信息就是组件实例 注意:只要想 ...
- vii
#!/bin/bash ] then vi exit fi ] then echo "参数太多了!" exit fi ] # 文件已存在,一律直接打开不作任何处理 then exi ...
- [BJWC2008]王之财宝
嘟嘟嘟 如果没有限制,而且必须选\(m\)件的话,就是隔板法\(C_{n + m - 1} ^ {m - 1}\)了.现在要选至多\(m\)件,那么就相当于新增一个板儿,分出的新的盒子表示" ...
- access函数
access函数是按照实际用户ID和实际组ID进行访问测试的.函数的定义如下: #include <unistd.h> int access(const char* pathname, i ...