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

本文主要是介绍linux-3.2.36内核启动2-setup_arch中的内存初始化1(arm平台 分析高端内存和初始化memblock),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

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

        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).

    而其余的空间都是高端内存的范围,即:3512G4G,但是为了避免越界等安全问题

    的考虑,高端内存又离开了低端内存8M空间,即从3G512M+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里有个memorystruct 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_INACTIVE0x44c9e71bUL

 

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

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

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

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

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

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

咱们看看它有什么

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

查看initrd的内容方法

我的是用cpio方法的,下面是我的pcinitrd的内容

[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》应该不会陌生吧,就是把bootcmdlineroot=后面的赋值到phys_initrd_startphys_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();更新memblockmemory_size的值

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

}

这篇关于linux-3.2.36内核启动2-setup_arch中的内存初始化1(arm平台 分析高端内存和初始化memblock)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/908156

相关文章

怎样通过分析GC日志来定位Java进程的内存问题

《怎样通过分析GC日志来定位Java进程的内存问题》:本文主要介绍怎样通过分析GC日志来定位Java进程的内存问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、GC 日志基础配置1. 启用详细 GC 日志2. 不同收集器的日志格式二、关键指标与分析维度1.

Linux中压缩、网络传输与系统监控工具的使用完整指南

《Linux中压缩、网络传输与系统监控工具的使用完整指南》在Linux系统管理中,压缩与传输工具是数据备份和远程协作的桥梁,而系统监控工具则是保障服务器稳定运行的眼睛,下面小编就来和大家详细介绍一下它... 目录引言一、压缩与解压:数据存储与传输的优化核心1. zip/unzip:通用压缩格式的便捷操作2.

Java内存分配与JVM参数详解(推荐)

《Java内存分配与JVM参数详解(推荐)》本文详解JVM内存结构与参数调整,涵盖堆分代、元空间、GC选择及优化策略,帮助开发者提升性能、避免内存泄漏,本文给大家介绍Java内存分配与JVM参数详解,... 目录引言JVM内存结构JVM参数概述堆内存分配年轻代与老年代调整堆内存大小调整年轻代与老年代比例元空

Linux中SSH服务配置的全面指南

《Linux中SSH服务配置的全面指南》作为网络安全工程师,SSH(SecureShell)服务的安全配置是我们日常工作中不可忽视的重要环节,本文将从基础配置到高级安全加固,全面解析SSH服务的各项参... 目录概述基础配置详解端口与监听设置主机密钥配置认证机制强化禁用密码认证禁止root直接登录实现双因素

MySQL中的表连接原理分析

《MySQL中的表连接原理分析》:本文主要介绍MySQL中的表连接原理分析,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、背景2、环境3、表连接原理【1】驱动表和被驱动表【2】内连接【3】外连接【4编程】嵌套循环连接【5】join buffer4、总结1、背景

在Linux终端中统计非二进制文件行数的实现方法

《在Linux终端中统计非二进制文件行数的实现方法》在Linux系统中,有时需要统计非二进制文件(如CSV、TXT文件)的行数,而不希望手动打开文件进行查看,例如,在处理大型日志文件、数据文件时,了解... 目录在linux终端中统计非二进制文件的行数技术背景实现步骤1. 使用wc命令2. 使用grep命令

python中Hash使用场景分析

《python中Hash使用场景分析》Python的hash()函数用于获取对象哈希值,常用于字典和集合,不可变类型可哈希,可变类型不可,常见算法包括除法、乘法、平方取中和随机数哈希,各有优缺点,需根... 目录python中的 Hash除法哈希算法乘法哈希算法平方取中法随机数哈希算法小结在Python中,

Java Stream的distinct去重原理分析

《JavaStream的distinct去重原理分析》Javastream中的distinct方法用于去除流中的重复元素,它返回一个包含过滤后唯一元素的新流,该方法会根据元素的hashcode和eq... 目录一、distinct 的基础用法与核心特性二、distinct 的底层实现原理1. 顺序流中的去重

Linux如何快速检查服务器的硬件配置和性能指标

《Linux如何快速检查服务器的硬件配置和性能指标》在运维和开发工作中,我们经常需要快速检查Linux服务器的硬件配置和性能指标,本文将以CentOS为例,介绍如何通过命令行快速获取这些关键信息,... 目录引言一、查询CPU核心数编程(几C?)1. 使用 nproc(最简单)2. 使用 lscpu(详细信

linux重启命令有哪些? 7个实用的Linux系统重启命令汇总

《linux重启命令有哪些?7个实用的Linux系统重启命令汇总》Linux系统提供了多种重启命令,常用的包括shutdown-r、reboot、init6等,不同命令适用于不同场景,本文将详细... 在管理和维护 linux 服务器时,完成系统更新、故障排查或日常维护后,重启系统往往是必不可少的步骤。本文