问题1:在系统启动是,arm linux内核如何知道系统中有多大的内存空间?
物理内存大小定义在设备树的memory节点中,系统启动时会通过dtb解析memory节点,从而获得内存大小。
内存大小定义
start_kernel->setup_arch->setup_machine_fdt->early_init_dt_scan_nodes->of_scan_flat_dt->early_init_dt_scan_memory
在函数early_init_dt_scan_memory会解析memory节点
/**
* early_init_dt_scan_memory - Look for an parse memory nodes
*/
int __init early_init_dt_scan_memory(unsigned long node, const char *uname,
int depth, void *data)
{
..........................
while ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) {
u64 base, size;
..............................................
printk(KERN_EMERG "base %llx, size %llx\n", base, size);
early_init_dt_add_memory_arch(base, size);
}
return 0;
}
sudo qemu-system-arm -M vexpress-a9 -m 1024M。如果-m 512M
则会输出base 60000000, size 20000000(512M)
感觉是二者取较小值
物理内存映射
void __init paging_init(const struct machine_desc *mdesc)
{
void *zero_page;
printk(KERN_EMERG "\r\npaging_init begin\n");
/* 给静态全局变量mem_types赋值 */
build_mem_type_table();
prepare_page_table();//将页表项清零
map_lowmem();//初始化页表,真正创建页表,重新建立从物理地址起始点到high_mem的起始点的一一映射
/* 建立DMA映射表 */
dma_contiguous_remap();
/* 为设备IO空间和中断向量表创建页表,并刷新TLB和缓存 */
devicemaps_init(mdesc);
/* 进行永久内存映射的初始化,存储在pkmap_page_table中 */
kmap_init();
/* TCM初始化,TCM是一个固定大小的RAM,紧密地耦合至处理器内核,提供与cache相当的性能 */
tcm_init();
top_pmd = pmd_off_k(0xffff0000);
/* allocate the zero page. */
zero_page = early_alloc(PAGE_SIZE);
/* bootmem_init初始化内存管理 */
bootmem_init();
/* 分配一个0页,该页用于写时复制机制。 */
empty_zero_page = virt_to_page(zero_page);
__flush_dcache_page(NULL, empty_zero_page);
}
prepare_page_table:把页表项清零
static inline void prepare_page_table(void)
{
unsigned long addr;
phys_addr_t end;
/*
* Clear out all the mappings below the kernel image.
*/
for (addr = 0; addr < MODULES_VADDR; addr += PMD_SIZE)
pmd_clear(pmd_off_k(addr));
#ifdef CONFIG_XIP_KERNEL
/* The XIP kernel is mapped in the module area -- skip over it */
addr = ((unsigned long)_etext + PMD_SIZE - 1) & PMD_MASK;
#endif
for ( ; addr < PAGE_OFFSET; addr += PMD_SIZE)
pmd_clear(pmd_off_k(addr));
/*
* Find the end of the first block of lowmem.
*/
end = memblock.memory.regions[0].base + memblock.memory.regions[0].size;
if (end >= arm_lowmem_limit)
end = arm_lowmem_limit;
/*
* Clear out all the kernel space mappings, except for the first
* memory bank, up to the vmalloc region.
*/
for (addr = __phys_to_virt(end);
addr < VMALLOC_START; addr += PMD_SIZE)
pmd_clear(pmd_off_k(addr));
printk(KERN_EMERG "\r\n MODULES_VADDR %lx, PAGE_OFFSET %lx, VMALLOC_START %lx\n", \
(unsigned long)MODULES_VADDR, (unsigned long)PAGE_OFFSET, (unsigned long)VMALLOC_START);
}
0x0--MODULES_VADDR ;
MODULES_VADDR--PAGE_OFFSET;
arm_lowmem_limit--VMALLOC_START
arm_lowmem_limit a0000000
map_lowmem:初始化页表
_stext和__init_end分别是内核代码段开始和结束
static void __init map_lowmem(void)
{
struct memblock_region *reg;
unsigned long kernel_x_start = round_down(__pa(_stext), SECTION_SIZE);
unsigned long kernel_x_end = round_up(__pa(__init_end), SECTION_SIZE);
printk(KERN_EMERG "\r\n arm_lowmem_limit %lx", arm_lowmem_limit);
printk(KERN_EMERG "\r\n kernel_x_start %lx,kernel_x_end %lx", kernel_x_start, kernel_x_end);
/* Map all the lowmem memory banks. */
for_each_memblock(memory, reg) {
phys_addr_t start = reg->base;
phys_addr_t end = start + reg->size;
struct map_desc map;
if (end > arm_lowmem_limit)
end = arm_lowmem_limit;
if (start >= end)
break;
printk(KERN_EMERG "\r\n start %lx, end %lx, size %lx\n", start, end, reg->size);
if (end < kernel_x_start || start >= kernel_x_end) {
/* 将物理地址转为物理page number, 这里是4k为一页 */
map.pfn = __phys_to_pfn(start);
map.virtual = __phys_to_virt(start);
map.length = end - start;
map.type = MT_MEMORY_RWX;
/* create_mapping进行线性映射 */
create_mapping(&map);
} else {
/* This better cover the entire kernel */
if (start < kernel_x_start) {
map.pfn = __phys_to_pfn(start);
map.virtual = __phys_to_virt(start);
map.length = kernel_x_start - start;
map.type = MT_MEMORY_RW;
create_mapping(&map);
}
/* 映射kernel image区域 */
map.pfn = __phys_to_pfn(kernel_x_start);
map.virtual = __phys_to_virt(kernel_x_start);
map.length = kernel_x_end - kernel_x_start;
map.type = MT_MEMORY_RWX;
create_mapping(&map);
/* 书上说映射低端内存,为什么是低端内存呢 */
if (kernel_x_end < end) {
map.pfn = __phys_to_pfn(kernel_x_end);
map.virtual = __phys_to_virt(kernel_x_end);
map.length = end - kernel_x_end;
map.type = MT_MEMORY_RW;
create_mapping(&map);
}
}
}
}
#define for_each_memblock(memblock_type, region) \
for (region = memblock.memblock_type.regions; \
region < (memblock.memblock_type.regions + memblock.memblock_type.cnt); \
region++)
可以看到这里的 map_lowmem函数里for_each_memblock(memory, reg)就是在使用之前memblock子系统里面的动态内存部分。在关于memblock子系统部分,关于memory_type为memory,即动态内存的部分,也只加入了一个区域,即dts里面定义的memory节点。因此这里也只循环了一次
static void __init map_lowmem(void)
{
....................
if (end < kernel_x_start || start >= kernel_x_end) {
/*不走这里*/.................
} else {
/* This better cover the entire kernel */
if (start < kernel_x_start) {
/* 也不走这里 */................................
}
/* 映射kernel image区域 */
map.pfn = __phys_to_pfn(kernel_x_start);
map.virtual = __phys_to_virt(kernel_x_start);
map.length = kernel_x_end - kernel_x_start;
map.type = MT_MEMORY_RWX;
create_mapping(&map);
/*
书上说映射低端内存,为什么是低端内存呢?
因为这部分内存是进行线性映射的,因此属于低端内存(zone_dma & zone_normal )
*/
if (kernel_x_end < end) {
map.pfn = __phys_to_pfn(kernel_x_end);
map.virtual = __phys_to_virt(kernel_x_end);
map.length = end - kernel_x_end;
map.type = MT_MEMORY_RW;
create_mapping(&map);
}
}
}
}
map_lowmem:
如上面代码所示,该函数会对两个区域进行映射(主要是两个区域的属性不一样)
这里感觉和书上写的不一样。那这1G的物理内存全部都拿去做线性映射了,没有所谓的高端内存
下图里面的lowmem刚好也是1024M
书上的arm_lowmem_limit=0x8f800000,意味着低端内存大小为0x8f800000-0x60000000=760M
那高端内存还有1024-760 = 264M.
那至此低端内存的全部都和页表建立了对应的映射关系。那高端内存呢?这个感觉是用到了在建立映射关系吧,比较这种映射关系不是固定的
因篇幅问题不能全部显示,请点此查看更多更全内容