linux版本:2.6.36
相關數據結構
arch/arm/include/asm/setup.h
#ifdef CONFIG_ARCH_LH7A40X
# define NR_BANKS 16
#else
# define NR_BANKS 8
#endif
struct membank {
unsigned long start;
unsigned long size;
unsigned int highmem;
};
struct meminfo {
int nr_banks;
struct membank bank[NR_BANKS];
};
linux內核的內存管理分三個階段。
1. 啓動---->bootmem初始化完成爲第一階段。此階段只能使用memblock_reserve函數分配內存。
此階段結束標誌爲:init_bootmem_done = 1.
2. bootmem初始化完--->buddy完成前。結束標誌爲mem_init_done = 1.
3. 所有內存初始化完畢,能夠用cache和buddy分配內存。
內存初始化步驟:
1. start_kernel---->setup_arch->pageing_init
|-->setup_per_cpu_areas
|-->build_all_zonelists
|-->mem_init
-->setup_per_cpu_pageset
setup_arch-->arm_memblock_init-->memblock_init
內存原始數據由u-boot傳入,對照本開發板uboot部份內存初始化函數,咱們知道uboot傳遞過來的tag->u.mem.start, tag->u.mem.size分別爲0x30000000,0x4000000,即內存起始地址是0x30000000,大小爲64M,start_arch獲取u-boot傳遞的參數地址後,調用了parse_tag_mem32函數對傳遞的內存參數處理:
556 static int __init parse_tag_mem32(const struct tag *tag)
557 {
558 return arm_add_memory(tag->u.mem.start, tag->u.mem.size);
559 }
560
561 __tagtable(ATAG_MEM, parse_tag_mem32);
如上可見,parse_tag_mem32函數調用arm_add_memory函數把RAM的start和size等參數保存到了meminfo結構的 meminfo結構體中。如今再來分析arm_add_memory
arm_add_memory定義以下(arch/arm/kernel/setup.c)
static int __init arm_add_memory(unsigned long start, unsigned long size)
{
struct membank *bank = &meminfo.bank[meminfo.nr_banks];
if (meminfo.nr_banks >= NR_BANKS) {
printk(KERN_CRIT "NR_BANKS too low, "
"ignoring memory at %#lx\n", start);
return -EINVAL;
}
/*
* Ensure that start/size are aligned to a page boundary.
* Size is appropriately rounded down, start is rounded up.
*/
size -= start & ~PAGE_MASK;
bank->start = PAGE_ALIGN(start);
bank->size = size & PAGE_MASK;
/*
* Check whether this memory region has non-zero size or
* invalid node number.
*/
if (bank->size == 0)
return -EINVAL;
meminfo.nr_banks++;
return 0;
}
通過這樣的處理,setup.c文件中的meminfo可就再也不是
struct meminfo meminfo = { 0, };
而是
struct meminfo meminfo = { 1,{0x30000000,0x4000000,0},{}, };
表示當前有一個內存區域,物理地址是從0x30000000開始,大小是64M,節點是0
處理完這些數據後,start_arch會調用第822行調用arm_memblock_init(&meminfo, mdesc);
先分析第1階段。
1. 初始化:setup_arch-->arm_memblock_init-->memblock_init
arch/arm/mm/init.c
270 void __init arm_memblock_init(struct meminfo *mi, struct machine_desc *mdesc)
271 {
272 int i;
273
274 memblock_init();
275 for (i = 0; i < mi->nr_banks; i++)
276 memblock_add(mi->bank[i].start, mi->bank[i].size);
277
278 /* Register the kernel text, kernel data and initrd with memblock. */
279 #ifdef CONFIG_XIP_KERNEL //配置內核XIP方式運行,避免把內核從Nor Flash 拷貝到主存SDRAM 空間,讓內核運行在低功耗的NOR flash 上,節省系統啓動時間,下降系統對SDRAM 的須要,減小電能消耗,使產品可以持續使用更長時間
280 memblock_reserve(__pa(_data), _end - _data);
281 #else
282 memblock_reserve(__pa(_stext), _end - _stext);
/*第282行,在0節點中保留內核鏡像部分的內存,大概是0x30108000到之後的5M左右(解壓後的內核)。
參看arch/arm/kernel/vmlinux.lds文件,
375 SECTIONS
376 {
377 . = 0xC0000000 + 0x00108000;
378 .init : { /* Init code and data */
379 _stext = .;
因此stext等於c0108000,對應物理地址30108000,
對_end再參看arch/arm/kernel/vmlinux.lds.S文件,它也在SECTIONS區,在233行定義
232 BSS_SECTION(0, 0, 0)
233 _end = .;
對於我移植的2.3.36內核,_end等於c0555b60,對應物理地址30555b60,在此語句後添加打印信息獲得
_end - _stext=44db60,4.512608M大小,因此這一句功能就是把0x30108000 ~ 0x30555b60這段(4.512608M)空間保留下來。
*/
283 #endif
284 #ifdef CONFIG_BLK_DEV_INITRD //本開發板沒配置
285 if (phys_initrd_size) {
286 memblock_reserve(phys_initrd_start, phys_initrd_size);
287
288 /* Now convert initrd to virtual addresses */
289 initrd_start = __phys_to_virt(phys_initrd_start);
290 initrd_end = initrd_start + phys_initrd_size;
291 }
292 #endif
293
294 arm_mm_memblock_reserve();//就是把0x30104000 ~ 0x30108000這段(16K)用於頁目錄的內容保留下來。
295
296 /* reserve any platform specific memblock areas */
297 if (mdesc->reserve)//對st2410中沒有定義
298 mdesc->reserve();
299
300 memblock_analyze();//計算內存塊大小
301 memblock_dump_all();//顯示
302 }
內存原始數據由u-boot傳入,在初始化完memblock_init後,arm_memblock_init函數中memblock_add調用 memblock_add_region加入原始內存數據,個人板子上配了64M內存,即:0x0000 0000->0x40000000,加完後的配置以下:
MEMBLOCK configuration:
rmo_size = 0x0
memory.size = 0x4000000
memory.cnt = 0x1
memory[0x0] 0x0000000030000000 - 0x0000000033ffffff, 0x4000000 bytes
reserved.cnt = 0x1
reserved[0x0] 0x0000000030104000 - 0x0000000030555b5f, 0x451b60 bytes
memblock_init()在mm/memblock.c裏面被定義。
void __init memblock_init(void)
{
/* Create a dummy zero size MEMBLOCK which will get coalesced away later.
* This simplifies the memblock_add() code below...
*/
memblock.memory.region[0].base = 0;
memblock.memory.region[0].size = 0;
memblock.memory.cnt = 1;
/* Ditto. */
memblock.reserved.region[0].base = 0;
memblock.reserved.region[0].size = 0;
memblock.reserved.cnt = 1;
}
其做用就是初始化memblock這個結構。將他們清空。memblock包含兩個重要的成員,分別是memblock.memory和memblock.reserved.其分別表明系統中可用的內存和已經被保留的內存。
memblock.memory和memblock.reserved被定義爲如下結構:include/linux/memblock.h
#define MAX_MEMBLOCK_REGIONS 128
struct memblock_property {
u64 base;
u64 size;
};
struct memblock_region {
unsigned long cnt;
u64 size;
struct memblock_property region[MAX_MEMBLOCK_REGIONS+1];
};
struct memblock {
unsigned long debug;
u64 rmo_size;
struct memblock_region memory;
struct memblock_region reserved;
};
mm/memblock.c
long memblock_add(u64 base, u64 size)
{
struct memblock_region *_rgn = &memblock.memory;
/* On pSeries LPAR systems, the first MEMBLOCK is our RMO region. */
if (base == 0)
memblock.rmo_size = size;
return memblock_add_region(_rgn, base, size);
}
static long memblock_add_region(struct memblock_region *rgn, u64 base, u64 size)
{
unsigned long coalesced = 0;
long adjacent, i;
if ((rgn->cnt == 1) && (rgn->region[0].size == 0)) {
rgn->region[0].base = base;
rgn->region[0].size = size;
return 0;
}
/* First try and coalesce this MEMBLOCK with another. */
for (i = 0; i < rgn->cnt; i++) {
u64 rgnbase = rgn->region[i].base;
u64 rgnsize = rgn->region[i].size;
if ((rgnbase == base) && (rgnsize == size))
/* Already have this region, so we're done */
return 0;
adjacent = memblock_addrs_adjacent(base, size, rgnbase, rgnsize);
if (adjacent > 0) {
rgn->region[i].base -= size;
rgn->region[i].size += size;
coalesced++;
break;
} else if (adjacent < 0) {
rgn->region[i].size += size;
coalesced++;
break;
}
}
if ((i < rgn->cnt - 1) && memblock_regions_adjacent(rgn, i, i+1)) {
memblock_coalesce_regions(rgn, i, i+1);
coalesced++;
}
if (coalesced)
return coalesced;
if (rgn->cnt >= MAX_MEMBLOCK_REGIONS)
return -1;
/* Couldn't coalesce the MEMBLOCK, so add it to the sorted table. */
for (i = rgn->cnt - 1; i >= 0; i--) {
if (base < rgn->region[i].base) {
rgn->region[i+1].base = rgn->region[i].base;
rgn->region[i+1].size = rgn->region[i].size;
} else {
rgn->region[i+1].base = base;
rgn->region[i+1].size = size;
break;
}
}
if (base < rgn->region[0].base) {
rgn->region[0].base = base;
rgn->region[0].size = size;
}
rgn->cnt++;
return 0;
}
memblock_add_region函數做用是將給定的物理地址所指定的memory region加入到指定的memblock(memblock.reserved或者是memblock.memory)中。新加入的memory region須要通過檢查,若是與原先的memory region有重疊,則須要合併在原先的memory region中,不然的話就新建一個memory region.
3. memblock_reserve用來分配內存頁。
以分配內核自己佔用的內存爲例:
/* Register the kernel text, kernel data and initrd with memblock. */
memblock_reserve(__pa(_stext), _end - _stext);
_stext,_end參見arch/arm/kernel/vmlinux.lds.S 連接腳本
long __init memblock_reserve(u64 base, u64 size)
{
struct memblock_region *_rgn = &memblock.reserved;
BUG_ON(0 == size);
return memblock_add_region(_rgn, base, size);
}
分配完以後的內存配置爲:
MEMBLOCK configuration:
rmo_size = 0x40000000
memory.size = 0x0
memory.cnt = 0x1
memory[0x0] 0x0000000000000000 - 0x000000003fffffff, 0x40000000 bytes
reserved.cnt = 0x1
reserved[0x0] 0x0000000000000000 - 0x0000000000xxxxx, _end - _stext bytes
若是分配有連續則進行合併。
幾回分配後的配置以下:
MEMBLOCK configuration:
rmo_size = 0x80000000
memory.size = 0x80000000
memory.cnt = 0x1
memory[0x0] 0x0000000000000000 - 0x000000007fffffff, 0x80000000 bytes
reserved.cnt = 0x6
reserved[0x0] 0x0000000000000000 - 0x00000000006b0fff, 0x6b1000 bytes
reserved[0x1] 0x0000000000ffa000 - 0x0000000000ffcfff, 0x3000 bytes
reserved[0x2] 0x000000002fbc4000 - 0x000000002fbdefff, 0x1b000 bytes
reserved[0x3] 0x000000002fbdfa88 - 0x000000002ffff4cc, 0x41fa45 bytes
reserved[0x4] 0x000000002fbe4000 - 0x000000002ffff4cd, 0x41b4ce bytes
reserved[0x5] 0x000000007ffff000 - 0x000000007fffffff, 0x1000 bytes
init
arch/arm/mm/mmu.c
/*
* Reserve the special regions of memory
*/
void __init arm_mm_memblock_reserve(void)
{
/*
* Reserve the page tables. These are already in use,
* and can only be in node 0.
*/
memblock_reserve(__pa(swapper_pg_dir), PTRS_PER_PGD * sizeof(pgd_t));
/*
相關的信息
swapper_pg_dir是初始化頁表虛擬地址,它在
./arch/arm/kernel/head.S定義
44: .globl swapper_pg_dir
45: .equ swapper_pg_dir, KERNEL_RAM_VADDR - 0x4000
./arch/arm/kernel/head.S:55:#define KERNEL_START KERNEL_RAM_VADDR
因此swapper_pg_dir等於0xc0104000,對應物理地址0x30104000
PTRS_PER_PGD在arch/arm/include/asm/pgtable.h第103行定義
#define PTRS_PER_PGD 2048
而pgd_t定義爲
typedef unsigned long pgd_t[2];
PTRS_PER_PGD * sizeof(pgd_t)=2048*8=16384的大小爲0x00004000 (16K)
就是把0x30104000 ~ 0x30108000這段(16K)用於頁目錄的內容保留下來。
*/
#ifdef CONFIG_SA1111 //2410沒定義
/*
* Because of the SA1111 DMA bug, we want to preserve our
* precious DMA-able memory...
*/
memblock_reserve(PHYS_OFFSET, __pa(swapper_pg_dir) - PHYS_OFFSET);
#endif
}
arch/arm/mm/init.c
#define MLK(b, t) b, t, ((t) - (b)) >> 10 //右移10位爲K
#define MLM(b, t) b, t, ((t) - (b)) >> 20 //右移20位爲M
#define MLK_ROUNDUP(b, t) b, t, DIV_ROUND_UP(((t) - (b)), SZ_1K)
printk(KERN_NOTICE "Virtual kernel memory layout:\n"
" vector : 0x%08lx - 0x%08lx (%4ld kB)\n"
" fixmap : 0x%08lx - 0x%08lx (%4ld kB)\n"
#ifdef CONFIG_MMU
" DMA : 0x%08lx - 0x%08lx (%4ld MB)\n"
#endif
" vmalloc : 0x%08lx - 0x%08lx (%4ld MB)\n"
" lowmem : 0x%08lx - 0x%08lx (%4ld MB)\n"
#ifdef CONFIG_HIGHMEM
" pkmap : 0x%08lx - 0x%08lx (%4ld MB)\n"
#endif
" modules : 0x%08lx - 0x%08lx (%4ld MB)\n"
" .init : 0x%p" " - 0x%p" " (%4d kB)\n"
" .text : 0x%p" " - 0x%p" " (%4d kB)\n"
" .data : 0x%p" " - 0x%p" " (%4d kB)\n",
MLK(UL(CONFIG_VECTORS_BASE), UL(CONFIG_VECTORS_BASE) +
(PAGE_SIZE)),
MLK(FIXADDR_START, FIXADDR_TOP),
#ifdef CONFIG_MMU
MLM(CONSISTENT_BASE, CONSISTENT_END),
#endif
MLM(VMALLOC_START, VMALLOC_END),
MLM(PAGE_OFFSET, (unsigned long)high_memory),
#ifdef CONFIG_HIGHMEM
MLM(PKMAP_BASE, (PKMAP_BASE) + (LAST_PKMAP) *
(PAGE_SIZE)),
#endif
MLM(MODULES_VADDR, MODULES_END),
MLK_ROUNDUP(__init_begin, __init_end),
MLK_ROUNDUP(_text, _etext),
MLK_ROUNDUP(_data, _edata));
系統啓動的時候打印出:
stext:c0108000,__pa_stext:30108000, _end - _stext=44db60
_end:c0555b60,__pa(_end)=30555b60
Memory: 64MB = 64MB total
Memory: 60536k/60536k available, 5000k reserved, 0K highmem
Virtual kernel memory layout:
vector : 0xffff0000 - 0xffff1000 ( 4 kB)
fixmap : 0xfff00000 - 0xfffe0000 ( 896 kB)
DMA : 0xffc00000 - 0xffe00000 ( 2 MB)
vmalloc : 0xc4800000 - 0xe0000000 ( 440 MB)
lowmem : 0xc0000000 - 0xc4000000 ( 64 MB)
modules : 0xbf000000 - 0xc0000000 ( 16 MB)
.init : 0xc0108000 - 0xc012e000 ( 152 kB)---->(系統啓動過程當中 以__init宏標識的函數佔用的空間 被vmlinux.lds標識爲 __init_begin __init_end 啓動init進程前被釋放掉 :Freeing init memory: 152K)
.text : 0xc012e000 - 0xc04dd000 (3772 kB)
.data : 0xc04fe000 - 0xc0527500 ( 166 kB)
Hierarchical RCU implementation.
啓動後查看內存命令顯示以下:
[root@localhost /]# free
total used free shared buffers
Mem: 60688 6644 54044 0 0
Swap: 0 0 0
Total: 60688 6644 54044
[root@localhost /]#
保留內存5000K中 linux內核佔用了152+3772+166=4090K
加上boot的時候參數等平臺空間佔用32K = 3149K
其它還差3464-3149=315K 沒有着落
其它還包括影射的向量表4K
還有多是系統管理內存的頁表佔用空間等其它的佔用
系統啓動成功後 Freeing init memory: 100K
因此用free命令看會多了100K
而free命令中看到的used基本是緩存 buffer佔用的,爲了提升i/o速度的緩存,不少都並不是真正應用
在我應用中去試圖malloc更多的內存的時候 used中不少都能被malloc出來的.
mm/memblock.c
void __init memblock_analyze(void)
{
int i;
memblock.memory.size = 0;
for (i = 0; i < memblock.memory.cnt; i++)
memblock.memory.size += memblock.memory.region[i].size;
}
mm/memblock.c
static void memblock_dump(struct memblock_region *region, char *name)
{
unsigned long long base, size;
int i;
pr_info(" %s.cnt = 0x%lx\n", name, region->cnt);
for (i = 0; i < region->cnt; i++) {
base = region->region[i].base;
size = region->region[i].size;
pr_info(" %s[0x%x]\t0x%016llx - 0x%016llx, 0x%llx bytes\n",
name, i, base, base + size - 1, size);
}
}
mm/memblock.c
void memblock_dump_all(void)
{
if (!memblock_debug)
return;
pr_info("MEMBLOCK configuration:\n");
pr_info(" rmo_size = 0x%llx\n", (unsigned long long)memblock.rmo_size);
pr_info(" memory.size = 0x%llx\n", (unsigned long long)memblock.memory.size);
memblock_dump(&memblock.memory, "memory");
memblock_dump(&memblock.reserved, "reserved");
}
22 static int memblock_debug;
23
24 static int __init early_memblock(char *p)
25 {
26 if (p && strstr(p, "debug"))
27 memblock_debug = 1;
28 return 0;
29 }
30 early_param("memblock", early_memblock);node