linux-3.2.36内核启动2-setup_arch中的内存初始化1(arm平台 分析高端内存和初始化memblock)【转】

简介:

转自:http://blog.csdn.net/tommy_wxie/article/details/17093307

上一篇微博留下了这几个函数,现在我们来分析它们

        sanity_check_meminfo();

        arm_memblock_init(&meminfo, mdesc);

        paging_init(mdesc);

        request_standard_resources(mdesc);

 

在上一微博有展现根据启动参数初始化meminfo,记录了物理内存的开始和大小

       sanity_check_meminfo();

有mmu的情况下这个函数才有意义,初始化高端内存,首先内核要选上这个

KernelFeatures下的

[*]High Memory Support

arch/arm/include/asm/setup.h

#ifdef CONFIG_ARCH_EP93XX

# define NR_BANKS 16

#else

# define NR_BANKS 8 三星当然是8

#endif

 

struct membank {

        phys_addr_t start;

        unsigned longsize;

        unsigned inthighmem;

};如果是高端内存highmem为1

 

struct meminfo {

        int nr_banks;

        struct membankbank[NR_BANKS];

};

extern struct meminfo meminfo;

我们现在的函数就是初始化meminfo这个全局变量

高端内存

    Linux内核的地址空间是3G~4G。假如说机器的内存为512M,那么内存的物

    理地址范围是:0~512,而映射到内核空间的范围是3G~3G+512M(可以叫low memory).

    而其余的空间都是高端内存的范围,即:3+512G~4G,但是为了避免越界等安全问题

    的考虑,高端内存又离开了低端内存8M空间,即从3G+512M+8M空间开始。linux内核又规定,高端内存至少为128M,即加入物理内存为1G,那么高端内存就是从896M~4G,即其最大地址:0xC0000000+896M,实际:0xC0000000+x(内存size)

简单举个例子,假设你有2G内存,而内核只有1G不能全部做线性映射,内核就会把前896M用于RAM线性映射,后128M可以通过更改映射关系访问剩下的内存。有三种方法:永久内核映射,临时映射,非连续内存分配(这些以后写关于内存管理的文章时再分析)。

没有全部贴

void __init sanity_check_meminfo(void)

{

        int i, j, highmem= 0;

 

        //wxl add

        printk(KERN_NOTICE"vmalloc_min = %lx\n", vmalloc_min);

打印结果

vmalloc_min = ee000000

vmalloc_min = (void *)(VMALLOC_END - SZ_128M);

arch/arm/mach-s3c2410/include/mach/vmalloc.h
#define VMALLOC_END 0xF6000000UL

 

0xF6000000-0x8000000=0xEE000000

3808M

 

        for (i = 0, j = 0;i < meminfo.nr_banks; i++) {

                structmembank *bank = &meminfo.bank[j];

                *bank =meminfo.bank[i];

 

#ifdef CONFIG_HIGHMEM

                _va()是物理地址转换虚拟地址

#define __virt_to_phys(x)      ((x) - PAGE_OFFSET + PHYS_OFFSET)

#define __phys_to_virt(x)      ((x) - PHYS_OFFSET + PAGE_OFFSET)

#define __va(x)                ((void *)__phys_to_virt((unsigned long)(x)))

下面的条件告诉了我们高端地址范围,大于等于vmalloc_min的好理解,小于PAGE_OFFSET是永久内核映射

                if(__va(bank->start) >= vmalloc_min ||

                   __va(bank->start) < (void *) PAGE_OFFSET)

                       highmem = 1;

                //wxl add

                printk(KERN_NOTICE "start:bank->start = %lx bank->size = %lx __va = %lx highmem = %d\n",(unsigned long)bank->start, (unsigned long)bank->size, (unsignedlong)__va(bank->start), highmem);

打印结果

start: bank->start = 30000000 bank->size = 4000000 __va =c0000000 highmem = 0

bank->start bank->size就是上一篇微博提到的

               bank->highmem = highmem;

 

                /*

                 * Splitthose memory banks which are partially overlapping

                 * thevmalloc area greatly simplifying things later.

                 */

                假设__va(bank->start) < vmalloc_min;它的大小可能会超过低端内存,也就是起始地址在低端,结束地址超过低端范围,那么就要把它分开,你可以简单看看代码

                if(__va(bank->start) < vmalloc_min &&

                   bank->size > vmalloc_min - __va(bank->start)) {

                        if(meminfo.nr_banks >= NR_BANKS) {

                               printk(KERN_CRIT "NR_BANKS too low, "

                                                 "ignoringhigh memory\n");

                        }else {

                               memmove(bank + 1, bank,

                                       (meminfo.nr_banks - i) * sizeof(*bank));

                               meminfo.nr_banks++;

                               i++;

                               bank[1].size -= vmalloc_min - __va(bank->start);

                               bank[1].start = __pa(vmalloc_min - 1) + 1;

                               bank[1].highmem = highmem = 1;

                               j++;

                        }

                       bank->size = vmalloc_min - __va(bank->start);

                }

                //wxl add

               printk(KERN_NOTICE "end: bank->start = %lx bank->size =%lx\n", (unsigned long)bank->start, (unsigned long)bank->size);

打印结果

end: bank->start = 30000000 bank->size = 4000000

 

#else

……

#endif

                重设低端内存限制

                if(!bank->highmem && bank->start + bank->size > lowmem_limit)

                        lowmem_limit =bank->start + bank->size;

 

                j++;

        }

……

 

 arm_memblock_init(&meminfo, mdesc);

        在此处按地址数据从小到大排序meminfo中的数据,并初始化全局的memblock数据。

void __init arm_memblock_init(struct meminfo *mi, structmachine_desc *mdesc)

{

        int i;

 

       sort(&meminfo.bank, meminfo.nr_banks, sizeof(meminfo.bank[0]),meminfo_cmp, NULL);排序不细看了,而且我的就一个bank

 

        memblock_init();

这个就是对memblock变量初始化,该赋初值的符初值,该清零的清零。说一点

memblock里有个memory是struct memblock_type

 

struct memblock_region {

        phys_addr_t base;

        phys_addr_t size;

};

 

struct memblock_type {

        unsigned longcnt;      /* number of regions */

        unsigned longmax;      /* size of the allocated array*/

        structmemblock_region *regions;

};

 

初始化

static struct memblock_regionmemblock_memory_init_regions[INIT_MEMBLOCK_REGIONS + 1] __initdata_memblock;

static struct memblock_regionmemblock_reserved_init_regions[INIT_MEMBLOCK_REGIONS + 1] __initdata_memblock;

memblock.memory.max = INIT_MEMBLOCK_REGIONS; 值为128

memblock.memory.cnt = 1;

memblock.memory.regions[0].base = 0;

memblock.memory.regions[0].size = 0;

memblock.memory.regions[INIT_MEMBLOCK_REGIONS].base =MEMBLOCK_INACTIVE;

MEMBLOCK_INACTIVE为0x44c9e71bUL

 

memblock.current_limit = MEMBLOCK_ALLOC_ANYWHERE;

#define MEMBLOCK_ALLOC_ANYWHERE      (~(phys_addr_t)0)这样看你是多少位系统了,不过我们只要知道是anywhere

memblock还有个reserved,和memory初始化的值一样。

 

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

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

long __init_memblock memblock_add(phys_addr_t base, phys_addr_tsize)

{

        return memblock_add_region(&memblock.memory,base, size);

我的base 0x30000000 size = 0x4000000;

}

 

static long __init_memblock memblock_add_region(structmemblock_type *type,

                                               phys_addr_t base, phys_addr_t size)

{

        phys_addr_t end =base + size;某bank的结束地址

        int i, slot = -1;

 

        /* First try andcoalesce this MEMBLOCK with others */

        for (i = 0; i <type->cnt; i++) {

                structmemblock_region *rgn = &type->regions[i];

                phys_addr_t rend = rgn->base +rgn->size;

 

                /* Exit ifthere's no possible hits */

                if(rgn->base > end || rgn->size == 0)检查是否在当前的bank中且size是否为0

                       break;

 

                /* Checkif we are fully enclosed within an existing

                 * block

                 */

                if(rgn->base <= base && rend >= end)检查是否超过当前block范围

                       return 0;

 

                /* Checkif we overlap or are adjacent with the bottom

                 * of a block.

                 */

                if (base< rgn->base && end >= rgn->base) {

                        /*If we can't coalesce, create a new block */

                        if(!memblock_memory_can_coalesce(…这个函数一定返回1所以省去,下同

                       

                        /*We extend the bottom of the block down to our

                         *base

                        */

                       rgn->base = base;

                       rgn->size = rend - base;

 

                        /* Return if we have nothingelse to allocate

                         *(fully coalesced)

                        */

                        if(rend >= end)

                               return 0;

 

                        /*We continue processing from the end of the

                         *coalesced block.

                        */

                       base = rend;

                       size = end - base;

                        上面这一段就是把去掉低端重叠区,

                }

 

                /* Now check if we overlap or areadjacent with the

                 * top ofa block

                 */

                顶部的重叠区去处

                if (base<= rend && end >= rend) {

                        /*If we can't coalesce, create a new block */

                        if(!memblock_memory_can_coalesce(…

 

                        size += (base - rgn->base);

                       base = rgn->base;

                       memblock_remove_region(type, i--);

        for (i = r; i < type->cnt - 1; i++) {

               type->regions[i].base = type->regions[i + 1].base;

               type->regions[i].size = type->regions[i + 1].size;

        }

        type->cnt--;

        有重叠说明连续的,就把它合并到一起,

                }

        }

 

        /* If the array isempty, special case, replace the fake

         * filler regionand return

         */

        if ((type->cnt== 1) && (type->regions[0].size == 0)) {

我的平台现在调用会执行到这

               type->regions[0].base = base; 0x30000000

               type->regions[0].size = size; 0x4000000

                return 0;

        }

 

 new_block:新的block

        /* If we are outof space, we fail. It's too late to resize the array

         * but then thisshouldn't have happened in the first place.

    */

        if(WARN_ON(type->cnt >= type->max))超过最大就返回

                return -1;

 

        /* Couldn'tcoalesce the MEMBLOCK, so add it to the sorted table. */

        不能合并我们按顺序存到regions中

        for (i =type->cnt - 1; i >= 0; i--) {

                if (base< type->regions[i].base) {

                       type->regions[i+1].base = type->regions[i].base;

                       type->regions[i+1].size = type->regions[i].size;

                } else {

                       type->regions[i+1].base = base;

                       type->regions[i+1].size = size;

                       slot = i + 1;

                       break;

                }

        }

        if (base <type->regions[0].base) {

               type->regions[0].base = base;

               type->regions[0].size = size;

                slot = 0;

        }

        type->cnt++;

 

        /* The array isfull ? Try to resize it. If that fails, we undo

         * our allocationand return an error

         */

        满了尝试重定义大小

        if (type->cnt== type->max && memblock_double_array(type)) {

               BUG_ON(slot < 0);

               memblock_remove_region(type, slot);

                return -1;

        }

 

        return 0;

}

 

看了源码其实就是把之前的bank信息存到memblock.memory.regions中。

 

        /* Register thekernel text, kernel data and initrd with memblock. */

Kernel XIP 原理如下,内核映像在Flash 设备上执行以后,只把映像中要读写的.data和.bss 拷贝到SDRAM 主存中,同时设置好系统的MMU,内核运行过程中,代码段.text 指向Flash 空间,.data 和.bss 指向SDRAM 主存空间。相对于全映射的执行方式,系统节省了解压缩和拷贝代码段的时间,节省了代码段占用的RAM 主存空间。

我的没有用这个东西。不过从下面你可以看到XIP没有吧text存入memblock

#ifdef CONFIG_XIP_KERNEL

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

#else

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

在System.map下

 

c00081e0 T _stext

c0318000 D _sdata

c0367db8 A _end

__pa(_stext) = 0x300081e0

 

#endif

long __init_memblock memblock_reserve(phys_addr_t base,phys_addr_t size)

{

        structmemblock_type *_rgn = &memblock.reserved;

 

        BUG_ON(0 == size);

 

        returnmemblock_add_region(_rgn, base, size);这个看上面

}

通过上面的我们可以算出,不过还是加个打印吧

       //wxl add

       printk(KERN_NOTICE "memory:\n");

       for (i = 0; i <memblock.memory.cnt; i++)

       {

           printk(KERN_NOTICE"regions[%d] base = %lx size = %lx\n", i, (unsignedlong)memblock.memory.regions[i].base, (unsigned long)memblock.memory.regions[i].size);

       }

 

       printk(KERN_NOTICE"reserved:\n");

       for (i = 0; i <memblock.reserved.cnt; i++)

       {

           printk(KERN_NOTICE"regions[%d] base = %lx size = %lx\n", i, (unsignedlong)memblock.reserved.regions[i].base, (unsigned long)memblock.reserved.regions[i].size);

       }

打印结果

memory:

regions[0] base = 30000000 size = 4000000

reserved:

regions[0] base = 300081e0 size = 35fbd8

用上面计算也是这个结果,到此reserved应该就是记录内核的大小,不过下面它还要做些事

 

下面和initrd的使用有关

为了能够使用RAM disk你的内核必须要支持RAMdisk,即:在编译内核时,要选中RAMdisk support这一选项,会在配置文件中定义CONFIG_BLK_DEV_RAM。
       为了让内核有能力在内核加载阶段就能装入RAMDISK,并运行其中的内容,要选中initial RAM disk(initrd) support 选项,会在配置文件中定义CONFIG_BLK_DEV_INITRD。

 

http://wenku.baidu.com/view/dc6dc785bceb19e8b8f6baba.html

此链接是一篇关于initramfs和initrd的文章,有兴趣看看

Initrd是一个临时文件系统。在某些没有存储设备的嵌入式系统中,initrd是永久根文件系统

它就是个文件系统,不过很小

initrd 中包含了实现这个目标所需要的目录和可执行程序的最小集合,例如将内核模块加载到内核中所使用的insmod 工具。

initrd 映像中包含了支持 Linux系统两阶段引导过程所需要的必要可执行程序和系统文件。

咱们看看它有什么

http://blog.163.com/dongfeng_114/blog/static/4664357420112452442211/

查看initrd的内容方法

我的是用cpio方法的,下面是我的pc中initrd的内容

[root@localhost tempfs]# ls

bin  dev  etc init  initrd.img  lib proc  sbin  sys sysroot

[root@localhost tempfs]# ls dev/

console  ptmx  ram1   tty   tty10  tty2 tty5  tty8   ttyS1 zero

mapper   ram   rtc    tty0  tty11  tty3 tty6  tty9   ttyS2

null     ram0  systty tty1  tty12  tty4 tty7  ttyS0  ttyS3

[root@localhost tempfs]# ls sbin/

dmraid  insmod  kpartx lvm  modprobe  nash

最后说一下uboot的bootargs 要设置initrd=addr,[Size]M

大家看看自己思考思考吧,我们看下面的内存处理

 

#ifdef CONFIG_BLK_DEV_INITRD

先说phys_initrd_size,它初始化定义是0

static int __init parse_tag_initrd(const struct tag *tag)

{

       printk(KERN_WARNING "ATAG_INITRD is deprecated; "

               "please update your bootloader.\n");

        phys_initrd_start= __virt_to_phys(tag->u.initrd.start);

        phys_initrd_size =tag->u.initrd.size;

        return 0;

}

 

__tagtable(ATAG_INITRD, parse_tag_initrd);

上面的东西看过我上一篇《linux内核启动1》应该不会陌生吧,就是把bootcmdline的root=后面的赋值到phys_initrd_start,phys_initrd_size;

        if(phys_initrd_size &&

           !memblock_is_region_memory(phys_initrd_start, phys_initrd_size)) {

memblock_is_region_memory函数就是和memblock.memory比较看在不在此内存里面

                pr_err("INITRD:0x%08lx+0x%08lx is not a memory region - disabling initrd\n",

                      phys_initrd_start, phys_initrd_size);

如果你看到这个打印,就是initrd不在可用内核范围内

               phys_initrd_start = phys_initrd_size = 0;

        }

        if(phys_initrd_size &&

           memblock_is_region_reserved(phys_initrd_start, phys_initrd_size)) {

memblock_is_region_reserved当然是和memblock.reserved比较

                pr_err("INITRD:0x%08lx+0x%08lx overlaps in-use memory region - disabling initrd\n",

                      phys_initrd_start, phys_initrd_size);

如果你看到这个打印,就是initrd和内核重叠了

               phys_initrd_start = phys_initrd_size = 0;

        }

上面的两个判断就是在可用内存内且不能和内核重叠

        if(phys_initrd_size) {

               memblock_reserve(phys_initrd_start, phys_initrd_size);上面已解释过

 

                /* Nowconvert initrd to virtual addresses */

               initrd_start = __phys_to_virt(phys_initrd_start);转换为虚拟地址

                initrd_end= initrd_start + phys_initrd_size;

        }

#endif

reserved又记录initrd信息

       arm_mm_memblock_reserve();

void __init arm_mm_memblock_reserve(void)

{

......

       memblock_reserve(__pa(swapper_pg_dir), SWAPPER_PG_DIR_SIZE);

……

存储页表空间

arm页表地址

c0004000 A swapper_pg_dir

arch/arm/kernel/head.S下

.equ    swapper_pg_dir,KERNEL_RAM_VADDR - PG_DIR_SIZE

#define PG_DIR_SIZE    0x4000

KERNEL_RAM_VADDR是0x30008000

       arm_dt_memblock_reserve();这个保存设备树的启动参数,不细看

 

        /* reserve anyplatform specific memblock areas */

        if(mdesc->reserve)对应平台自定义的block区

               mdesc->reserve();

 

       memblock_analyze();更新memblock中memory_size的值

       memblock_dump_all();这个就是打印reserved和memory,和我中间加的打印东西差不多








本文转自张昺华-sky博客园博客,原文链接:http://www.cnblogs.com/sky-heaven/p/6307639.html,如需转载请自行联系原作者

相关文章
|
14天前
|
Web App开发 监控 JavaScript
监控和分析 JavaScript 内存使用情况
【10月更文挑战第30天】通过使用上述的浏览器开发者工具、性能分析工具和内存泄漏检测工具,可以有效地监控和分析JavaScript内存使用情况,及时发现和解决内存泄漏、过度内存消耗等问题,从而提高JavaScript应用程序的性能和稳定性。在实际开发中,可以根据具体的需求和场景选择合适的工具和方法来进行内存监控和分析。
|
1月前
|
编译器 C语言
动态内存分配与管理详解(附加笔试题分析)(上)
动态内存分配与管理详解(附加笔试题分析)
49 1
|
2月前
|
程序员 编译器 C++
【C++核心】C++内存分区模型分析
这篇文章详细解释了C++程序执行时内存的四个区域:代码区、全局区、栈区和堆区,以及如何在这些区域中分配和释放内存。
52 2
|
3月前
|
Ubuntu Linux
查看Linux系统架构的命令,查看linux系统是哪种架构:AMD、ARM、x86、x86_64、pcc 或 查看Ubuntu的版本号
查看Linux系统架构的命令,查看linux系统是哪种架构:AMD、ARM、x86、x86_64、pcc 或 查看Ubuntu的版本号
856 3
|
9天前
|
开发框架 监控 .NET
【Azure App Service】部署在App Service上的.NET应用内存消耗不能超过2GB的情况分析
x64 dotnet runtime is not installed on the app service by default. Since we had the app service running in x64, it was proxying the request to a 32 bit dotnet process which was throwing an OutOfMemoryException with requests >100MB. It worked on the IaaS servers because we had the x64 runtime install
|
19天前
|
Web App开发 JavaScript 前端开发
使用 Chrome 浏览器的内存分析工具来检测 JavaScript 中的内存泄漏
【10月更文挑战第25天】利用 Chrome 浏览器的内存分析工具,可以较为准确地检测 JavaScript 中的内存泄漏问题,并帮助我们找出潜在的泄漏点,以便采取相应的解决措施。
128 9
|
23天前
|
并行计算 算法 IDE
【灵码助力Cuda算法分析】分析共享内存的矩阵乘法优化
本文介绍了如何利用通义灵码在Visual Studio 2022中对基于CUDA的共享内存矩阵乘法优化代码进行深入分析。文章从整体程序结构入手,逐步深入到线程调度、矩阵分块、循环展开等关键细节,最后通过带入具体值的方式进一步解析复杂循环逻辑,展示了通义灵码在辅助理解和优化CUDA编程中的强大功能。
|
1月前
|
程序员 编译器 C语言
动态内存分配与管理详解(附加笔试题分析)(下)
动态内存分配与管理详解(附加笔试题分析)(下)
46 2
|
2月前
|
算法 程序员 Python
程序员必看!Python复杂度分析全攻略,让你的算法设计既快又省内存!
在编程领域,Python以简洁的语法和强大的库支持成为众多程序员的首选语言。然而,性能优化仍是挑战。本文将带你深入了解Python算法的复杂度分析,从时间与空间复杂度入手,分享四大最佳实践:选择合适算法、优化实现、利用Python特性减少空间消耗及定期评估调整,助你写出高效且节省内存的代码,轻松应对各种编程挑战。
41 1
|
2月前
|
存储 Prometheus NoSQL
Redis 内存突增时,如何定量分析其内存使用情况
【9月更文挑战第21天】当Redis内存突增时,可采用多种方法分析内存使用情况:1)使用`INFO memory`命令查看详细内存信息;2)借助`redis-cli --bigkeys`和RMA工具定位大键;3)利用Prometheus和Grafana监控内存变化;4)优化数据类型和存储结构;5)检查并调整内存碎片率。通过这些方法,可有效定位并解决内存问题,保障Redis稳定运行。