Linux 下启动时的内存分析--引用

//1.arch/arm/mm/Init.c->bootmem_init()
void __init bootmem_init(struct meminfo *mi)
{
    
struct node_info node_info[NR_NODES], *np = node_info;
    
unsigned int bootmap_pages, bootmap_pfn, map_pg;
    
int node, initrd_node;
    
//计算为了管理所有mem内存,需管理位图占据页数目bootmap_pages,np中存储mem对应的页帧号
    bootmap_pages
= find_memend_and_nodes(mi, np);
    
//查找存放位图管理页的物理页帧号,实际是存放到_end后的后续页中
    bootmap_pfn
= find_bootmap_pfn(0, mi, bootmap_pages);
    
//检查initrd的合法性,同时将initrd所在内存banknode返回给initrd_node
    initrd_node
= check_initrd(mi);
    map_pg
= bootmap_pfn;//位图页帧号
    np
+= numnodes - 1;
    
for (node = numnodes - 1; node >= 0; node--, np--) {
        
/*
         * If there are no pages in this node, ignore it.
         * Note that node 0 must always have some pages.
         */

        
if (np->end == 0) {
            
if (node == 0)
                BUG
();
            
continue;
        
}
        
//map_pg开始的位图管理空间全部置0xff
        init_bootmem_node
(NODE_DATA(node), map_pg, np->start, np->end);
        
//释放虚拟地址node_bootmem_map开始的位图管理的所有页,使相应页可用[gliethttp]
        free_bootmem_node_bank
(node, mi);
        map_pg
+= np->bootmap_pages;
        
/*
         * If this is node 0, we need to reserve some areas ASAP -
         * we may use bootmem on node 0 to setup the other nodes.
         */

        
if (node == 0)//我的at91rm9200开发板仅仅有一个node=0
//kernel自身和位图管理页占用的页对应的页位图置1,标识相应页已被占用
            reserve_node_zero
(bootmap_pfn, bootmap_pages);
    
}
#ifdef CONFIG_BLK_DEV_INITRD//在我的at91rm9200开发板中,initrd是开启的
//并且phys_initrd_start=0x21100000
//phys_initrd_size=6000000=0x5B8D80=5.73M
//initrd_node=0;
    
if (phys_initrd_size && initrd_node >= 0) {
//initrd占用的页对应的页位图置1,标识相应页已被占用
        reserve_bootmem_node
(NODE_DATA(initrd_node), phys_initrd_start,
                 phys_initrd_size
);
        initrd_start
= __phys_to_virt(phys_initrd_start);//转成虚拟地址
        initrd_end
= initrd_start + phys_initrd_size;
    
}
#endif
    
if (map_pg != bootmap_pfn + bootmap_pages)//保证所有bootmap都已经被遍历
        BUG
();
}
//----------------------------------------
//2.arch/arm/mm/Init.c->find_memend_and_nodes()
static unsigned int __init find_memend_and_nodes(struct meminfo *mi, struct node_info *np)
{unsigned int i, bootmem_pages = 0, memend_pfn = 0;
    
for (i = 0; i < NR_NODES; i++) {//默认失效
        np
[i].start = -1U;
        np
[i].end = 0;
        np
[i].bootmap_pages = 0;
    
}
    
for (i = 0; i < mi->nr_banks; i++) {
        
unsigned long start, end;
        
int node;
        
if (mi->bank[i].size == 0) {
            mi
->bank[i].node = -1;//banknode无效-1
            
continue;
        
}
        node
= mi->bank[i].node;//at91rm9200dkmem连续node=0
        
if (node >= numnodes) {
            numnodes
= node + 1;
            
if (numnodes > NR_NODES)
                BUG
();
        
}
//获取当前bankpfns
//#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK)//页边界对齐
//define O_PFN_UP(x) (PAGE_ALIGN(x) >> PAGE_SHIFT)
        start
= O_PFN_UP(mi->bank[i].start);//获取该bank.start对应物理页帧号
//同理,//获取该bank.end对应物理页帧号
        end
= O_PFN_DOWN(mi->bank[i].start + mi->bank[i].size);
        
if (np[node].start > start)
            np
[node].start = start;//存储
        
if (np[node].end < end)
            np
[node].end = end;//存储
        
if (memend_pfn < end)
            memend_pfn
= end;
    
}
    
for (i = 0; i < numnodes; i++) {
        
if (np[i].end == 0)
            
continue;
//bootmem_bootmap_pages计算pages个页需要多少个页来存储其位图管理信息
        np
[i].bootmap_pages = bootmem_bootmap_pages(np[i].end - np[i].start);
        bootmem_pages
+= np[i].bootmap_pages;//累计位图管理信息页总数
    
}
    
/*
     * This doesn't seem to be used by the Linux memory
     * manager any more. If we can get rid of it, we
     * also get rid of some of the stuff above as well.
     */

    max_low_pfn
= memend_pfn - O_PFN_DOWN(PHYS_OFFSET);
// max_pfn = memend_pfn - O_PFN_DOWN(PHYS_OFFSET);
    mi
->end = memend_pfn << PAGE_SHIFT;//所管理的物理内存结束地址
    
return bootmem_pages;
}
//----------------------------------------
//3.mm/Bootmem.c->bootmem_bootmap_pages()
//计算pages个页需要多少个页来存储其位图管理信息
unsigned long __init bootmem_bootmap_pages (unsigned long pages)
{unsigned long mapsize;
    mapsize
= (pages+7)/8;//所需8bits个数
    mapsize
= (mapsize + ~PAGE_MASK) & PAGE_MASK;//mapsize个字节页对齐
    mapsize
>>= PAGE_SHIFT;//mapsize个字节对应页数目
    
return mapsize;
}
//----------------------------------------
//4.arch/arm/mm/Init.c->find_bootmap_pfn()
//查找存放位图管理页的物理页帧号-init_mm.brk
static unsigned int __init
find_bootmap_pfn
(int node, struct meminfo *mi, unsigned int bootmap_pages)
{unsigned int start_pfn, bank, bootmap_pfn;
    start_pfn
= V_PFN_UP(&_end);//_end虚拟内存转换为对应的物理页帧号
    bootmap_pfn
= 0;
    
for (bank = 0; bank < mi->nr_banks; bank ++) {
        
unsigned int start, end;
        
if (mi->bank[bank].node != node)
            
continue;
        start
= O_PFN_UP(mi->bank[bank].start);
        end
= O_PFN_DOWN(mi->bank[bank].size +
                 mi
->bank[bank].start);
        
if (end < start_pfn)
            
continue;
        
if (start < start_pfn)
            start
= start_pfn;//_end对应init_mm.brk临界点

        
if (end <= start)
            
continue;
        
if (end - start >= bootmap_pages) {
            bootmap_pfn
= start;//init_mm.brk开始存放位图管理页
            
break;
        
}
    
}
    
if (bootmap_pfn == 0)
        BUG
();
    
return bootmap_pfn;//ok[gliethttp]
}
//----------------------------------------
//5.arch/arm/mm/Init.c->check_initrd()
//检查initrd的合法性
static int __init check_initrd(struct meminfo *mi)
{
    
int initrd_node = -2;
    
unsigned long end = phys_initrd_start + phys_initrd_size;
#ifdef CONFIG_BLK_DEV_INITRD
    
/*
     * Make sure that the initrd is within a valid area of
     * memory.
     */

    
if (phys_initrd_size) {
        
unsigned int i;
        initrd_node
= -1;
        
for (i = 0; i < mi->nr_banks; i++) {
            
unsigned long bank_end;
            bank_end
= mi->bank[i].start + mi->bank[i].size;
            
if (mi->bank[i].start <= phys_initrd_start &&
             end
<= bank_end)
                initrd_node
= mi->bank[i].node;
        
}
    
}
    
if (initrd_node == -1) {
        printk
(KERN_ERR "initrd (0x%08lx - 0x%08lx) extends beyond "
         
"physical memory - disabling initrd/n",
         phys_initrd_start
, end);
        phys_initrd_start
= phys_initrd_size = 0;
    
}
#endif
    
return initrd_node;//initrd所在内存bank对应的node返回
}
//----------------------------------------
//6.mm/Bootmem.c->init_bootmem_node()
unsigned long __init init_bootmem_node (pg_data_t *pgdat, unsigned long freepfn, unsigned long startpfn, unsigned long endpfn)
{
    
return(init_bootmem_core(pgdat, freepfn, startpfn, endpfn));
}
//mm/Bootmem.c->init_bootmem_core()
static unsigned long __init init_bootmem_core (pg_data_t *pgdat,
    
unsigned long mapstart, unsigned long start, unsigned long end)
{
    bootmem_data_t
*bdata = pgdat->bdata;//读取&node_bootmem_data[0]
    
unsigned long mapsize = ((end - start)+7)/8;
    pgdat
->node_next = pgdat_list;
    pgdat_list
= pgdat;
    mapsize
= (mapsize + (sizeof(long) - 1UL)) & ~(sizeof(long) - 1UL);//4字节对齐
    bdata
->node_bootmem_map = phys_to_virt(mapstart << PAGE_SHIFT);//位图页帧号转为系统用的虚拟地址
    bdata
->node_boot_start = (start << PAGE_SHIFT);//map所处物理内存起始地址
    bdata
->node_low_pfn = end;//map所处物理内存结束地址
    
//0xff填充map位图页帧后,mapsize个字节数据
    
//arch/arm/lib/memset.S->memset(),对虚拟地址bdata->node_bootmem_map进行赋值
    
memset(bdata->node_bootmem_map, 0xff, mapsize);
    
return mapsize;
}
//----------------------------------------
//7.mm/Bootmem.c->free_bootmem_node_bank()
static inline void free_bootmem_node_bank(int node, struct meminfo *mi)
{
    pg_data_t
*pgdat = NODE_DATA(node);//读取&node_bootmem_data[0]
    
int bank;
    
for (bank = 0; bank < mi->nr_banks; bank++)
        
if (mi->bank[bank].node == node)
            free_bootmem_node
(pgdat, mi->bank[bank].start,
                     mi
->bank[bank].size);
}
void __init free_bootmem_node (pg_data_t *pgdat, unsigned long physaddr, unsigned long size)
{
    
return(free_bootmem_core(pgdat->bdata, physaddr, size));
}
static void __init free_bootmem_core(bootmem_data_t *bdata, unsigned long addr, unsigned long size)
{
    
unsigned long i;
    
unsigned long start;
    
/*
     * round down end of usable mem, partially free pages are
     * considered reserved.
     */

    
unsigned long sidx;
    
unsigned long eidx = (addr + size - bdata->node_boot_start)/PAGE_SIZE;
    
unsigned long end = (addr + size)/PAGE_SIZE;
    
if (!size) BUG();
    
if (end > bdata->node_low_pfn)//一般相等
        BUG
();
    start
= (addr + PAGE_SIZE-1) / PAGE_SIZE;//mem起始物理地址对应的页帧号
    sidx
= start - (bdata->node_boot_start/PAGE_SIZE);//起始大小
    
for (i = sidx; i < eidx; i++) {///sidx=0,eidx=size/PAGE_SIZE[gliethttp]
//0各位图管理位,使相应页可用
//i=4,那么第4物理页可用,0bdata->node_bootmem_map[0]的第4
        
if (!test_and_clear_bit(i, bdata->node_bootmem_map))//node_bootmem_map位图管理起始虚拟地址
            BUG
();
    
}
}
///arch/arm/lib/testclearbit.S->test_and_clear_bit()
ENTRY
(test_and_clear_bit)
        add r1
, r1, r0, lsr #3//获取第r0,对应字节对应的地址
        
and r3, r0, #7//获取字节中偏移
        mov r0
, #1
//include/asm-arm/proc-armv/Assembler.h->save_and_disable_irqs[gliethttp]
//.macro save_and_disable_irqs, oldcpsr, temp
//mrs /oldcpsr, cpsr//
//mov /temp, #I_BIT | MODE_SVC
//msr cpsr_c, /temp
//.endm
        save_and_disable_irqs ip
, r2//cpsr保存到ip
        ldrb r2
, [r1]//取出字节数据
        tst r2
, r0, lsl r3//先测一次,我感觉没用
        bic r2
, r2, r0, lsl r3//r2的第r3位清0,成功清0Z=0,因为硬件故障未能成功那么Z=1,返回后会halt系统
        strb r2
, [r1]//存储清0后的结果
        restore_irqs ip
//恢复cpsr
        moveq r0
, #0//如果因为硬件故障导致不能清0,那么r0=0;0失败
        RETINSTR
(mov,pc,lr)
//----------------------------------------
//8.arch/arm/mm/Init.c->reserve_node_zero()
//kernel自身和位图管理页占用的页对应的页位图置1,标识相应页已被占用
static __init void reserve_node_zero(unsigned int bootmap_pfn, unsigned int bootmap_pages)
{
    pg_data_t
*pgdat = NODE_DATA(0);//获取node0的所有物理内存管理单元pgdat
//对于_end_stext,参考arch/arm/vmlinux-armv.lds.in链接脚本
//_endinit_mm.brk,kernel最末端地址
//__pa(&_stext)物理地址开始的&_end - &_stext个数据,对应页位图置1标识相应页已被占用
    reserve_bootmem_node
(pgdat, __pa(&_stext), &_end - &_stext);
#ifdef CONFIG_CPU_32
//arch/arm/kernel/head-armv.s4M保留页swapper_pg_dir,我的at91rm9200板子对应的物理地址为0x20004000~0x20008000
//swapper_pg_dir~swapper_pg_dir+4k*4的页对应的页位图置1标识相应页已被占用
//#define PTRS_PER_PGD 4096
    reserve_bootmem_node
(pgdat, __pa(swapper_pg_dir),
             PTRS_PER_PGD
* sizeof(pgd_t));
#endif
//_endinit_mm.brk之后存放node物理页位图的位图管理空间也保护起来
//bootmap_pfn << PAGE_SHIFT~(bootmap_pfn << PAGE_SHIFT)+(bootmap_pages << PAGE_SHIFT)的页对应的页位图置1标识相应页已被占用
    reserve_bootmem_node
(pgdat, bootmap_pfn << PAGE_SHIFT,
             bootmap_pages
<< PAGE_SHIFT);
//以下代码在at91rm9200dk下均不被编译进vmlinuz
    
if (machine_is_integrator())//if(0),否则会因为reserve_bootmem_node回环,halt系统
        reserve_bootmem_node
(pgdat, 0, __pa(swapper_pg_dir));
    
if (machine_is_archimedes() || machine_is_a5k())
        reserve_bootmem_node
(pgdat, 0x02000000, 0x00080000);
    
if (machine_is_edb7211() || machine_is_fortunet())
        reserve_bootmem_node
(pgdat, 0xc0000000, 0x00020000);
    
if (machine_is_p720t())
        reserve_bootmem_node
(pgdat, PHYS_OFFSET, 0x00014000);
#ifdef CONFIG_SA1111//SA1111
    reserve_bootmem_node
(pgdat, PHYS_OFFSET, __pa(swapper_pg_dir)-PHYS_OFFSET);
#endif
}
void __init reserve_bootmem_node (pg_data_t *pgdat, unsigned long physaddr, unsigned long size)
{//使pgdat管理的物理内存,physaddr~(physaddr+size)之间的物理内存不可使用--占用保留
 
//physaddr~(physaddr+size)对应页位图置1标识相应页已被占用
    reserve_bootmem_core
(pgdat->bdata, physaddr, size);
}
static void __init reserve_bootmem_core(bootmem_data_t *bdata, unsigned long addr, unsigned long size)
{
    
unsigned long i;
    
/*
     * round up, partially reserved pages are considered
     * fully reserved.
     */

//bdata->node_boot_startbdata对应node物理内存的起始地址
    
unsigned long sidx = (addr - bdata->node_boot_start)/PAGE_SIZE;//计算addr所处页帧号
    
unsigned long eidx = (addr + size - bdata->node_boot_start +
                         PAGE_SIZE
-1)/PAGE_SIZE;//计算addr+size对应的页帧号
    
unsigned long end = (addr + size + PAGE_SIZE-1)/PAGE_SIZE;
    
if (!size) BUG();
    
if (sidx < 0)
        BUG
();
    
if (eidx < 0)
        BUG
();
    
if (sidx >= eidx)//数据回环
        BUG
();
//bdata->node_low_pfn为本node物理内存的地址上限
    
if ((addr >> PAGE_SHIFT) >= bdata->node_low_pfn)
        BUG
();
    
if (end > bdata->node_low_pfn)
        BUG
();
//条件符合,那么实行保护措施
//sidx页到eidx页对应的位图置1,位图置1标识相应页已被占用(保护起来,不被kernel使用)
    
for (i = sidx; i < eidx; i++)
        
if (test_and_set_bit(i, bdata->node_bootmem_map))
            printk
("hm, page %08lx reserved twice./n", i*PAGE_SIZE);
}
///arch/arm/lib/testclearbit.S->test_and_set_bit()
//ENTRY(test_and_set_bit)
// add r1, r1, r0, lsr #3//获取第r0位对应的字节地址
// and r3, r0, #7//计算处于字节的第几位
// mov r0, #1
// save_and_disable_irqs ip, r2//ip=cpsr
// ldrb r2, [r1]
// tst r2, r0, lsl r3
//orr操作,成功操作后Z=0,因为硬件故障未能成功那么Z=1,返回后会halt系统
// orr r2, r2, r0, lsl r3//r2中的第r3位置1
// strb r2, [r1]//回写
// restore_irqs ip
// moveq r0, #0
// RETINSTR(mov,pc,lr)
//include/asm-arm/proc-armv/Assembler.h->save_and_disable_irqs[gliethttp]
//.macro save_and_disable_irqs, oldcpsr, temp
//mrs /oldcpsr, cpsr//
//mov /temp, #I_BIT | MODE_SVC

阅读更多
想对作者说点什么? 我来说一句

jProfiler7 java内存分析 linux版本

2012年05月29日 48.65MB 下载

c# 引用 类型 参数-内存分析

2010年11月30日 49KB 下载

linux 内存运行原理自己的总结

2018年03月29日 42KB 下载

c# 值类型 引用类型 内存分析

2010年11月30日 121KB 下载

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭