linux内核源码分析--内核启动之,Linux内核源码分析之setup_arch (二)

1. 概述

接着上一篇《Linux内核源码分析之setup_arch (一)》继续分析,本文首先分析arm_memblock_init函数,而后分析内核启动阶段的是如何进行内存管理的。node

2. arm_memblock_init

该函数的功能比较简单,主要就是把meminfo中记录的内存条信息添加到memblock.memory中,而后把内核镜像所在内存区域添加到memblock.reserved中,arm_mm_memblock_reserve把页表所在内存区域添加到memblock.reserved中;若是使用了设备树,则使用arm_dt_memblock_reserve来保留所占用的内存,最后则是调用CPU相关的mdesc->reserve,其对应的调用为cpu_mem_reserve,该函数定义在cpu.c中。linux

/* arch/arm/mm/init.c */

void __init arm_memblock_init(...) {

for (i = 0; i < mi->nr_banks; i++)

memblock_add(mi->bank[i].start, mi->bank[i].size);

memblock_reserve(__pa(_stext), _end - _stext);

arm_mm_memblock_reserve();

arm_dt_memblock_reserve();

if (mdesc->reserve)

mdesc->reserve();

arm_memblock_steal_permitted = false;

memblock_allow_resize();

memblock_dump_all();

}

/* include/kernel/memblock.h */

struct memblock {

phys_addr_t current_limit;

struct memblock_type memory;

struct memblock_type reserved;

};

3. memblock_alloc

接下来就该执行paging_init函数了,在分析paging_init以前先来点内核启动阶段的内存管理相关的内容。从arm_memblock_init开始引入memblock数据结构,其做用是实现内核启动初期的内存管理功能,严格来讲,其生命周期到paging_init::bootmem_init为止,memblock_alloc调用流程以下。数据结构

a95924d8905aca8a145ba4b999bfd086.png

实际查找空闲内存的函数为memblock_find_in_range_node,而该函数中真正实现空闲内存查找的是for_each_free_mem_range_reverse这个宏定义。函数

/* mm/memblock.c */

phys_addr_t memblock_find_in_range_node(...)

{

...

for_each_free_mem_range_reverse(i, nid, &this_start, &this_end, NULL) {

...

if (cand >= this_start)

return cand;

}

return 0;

}

该宏定义以下,然而其中又嵌套了一个函数Orz...源码分析

/* include/linux/memblock.h */

#define for_each_free_mem_range_reverse(i, nid, p_start, p_end, p_nid) \

for (i = (u64)ULLONG_MAX, \

__next_free_mem_range_rev(&i, nid, p_start, p_end, p_nid); \

i != (u64)ULLONG_MAX; \

__next_free_mem_range_rev(&i, nid, p_start, p_end, p_nid))

首先须要说明的是,memblock.reserved标识的区域表示的是已被占用的内存区域,memblock.memory中记录的是内存条信息。如今回到__next_free_mem_range_rev函数,代码段(1)(2)的目的是找出内存条上两个reserved区域之间的内存区域,即空闲区域。找到以后再通过代码段(3)对空闲区域的起始地址和结束地址进行修正,由于代码段(1)(2)只能保证空闲区与当前内存条存在交集,并不能保证该空闲区域彻底处于当前内存条之中,主要缘由在于没法保证这两个reserved区域都在当前内存条上。this

/* mm/memblock.c */

void __init_memblock __next_free_mem_range_rev(...)

{

struct memblock_type *mem = &memblock.memory;

struct memblock_type *rsv = &memblock.reserved;

...

/* (1) */

for ( ; mi >= 0; mi--) {

struct memblock_region *m = &mem->regions[mi];

phys_addr_t m_start = m->base;

phys_addr_t m_end = m->base + m->size;

...

/* (2) */

for ( ; ri >= 0; ri--) {

struct memblock_region *r = &rsv->regions[ri];

phys_addr_t r_start = ri ? r[-1].base + r[-1].size : 0;

phys_addr_t r_end = ri < rsv->cnt ? r->base : ULLONG_MAX;

...

/* (3) */

if (m_end > r_start) {

if (out_start)

*out_start = max(m_start, r_start);

if (out_end)

*out_end = min(m_end, r_end);

if (out_nid)

*out_nid = memblock_get_region_node(m);

...

return;

}

}

}

*idx = ULLONG_MAX;

}

至此,空闲区域的查找基本就结束了,回到memblock_find_in_range_node函数中,再检查一下该区域的起始地址和结束地址是否合法等等,最终就申请到了所请求大小的内存区域,最后只须要将这块内存区域标记为reserved状态就结束了内存分配的整个过程了。code

/* mm/memblock.c */

int memblock_reserve(phys_addr_t base, phys_addr_t size)

{

struct memblock_type *_rgn = &memblock.reserved;

return memblock_add_region(_rgn, base, size, MAX_NUMNODES);

}

4. 总结

arm_memblock_init函数首先把记录在meminfo记录的内存条信息转移到memblock.memory中,而后把已经使用的内存区域记录到memblock.reserved中,主要包括内核镜像所占用区域、页表区域以及设备树;

memblock_alloc经过memblock中的memory和reserved中记录的信息进行内存管理,每次申请到内存以后都在memblock.reserved中进行记录。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值