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,和我中间加的打印东西差不多

 

时间: 2024-09-10 08:30:33

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

Linux用户空间与内核空间(理解高端内存)

Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针传递数据,因为Linux使用的虚拟内存机制,用户空间的数据可能被换出,当内核空间使用用户空间指针时,对应的数据可能不在内存中.   Linux内核地址映射模型 x86 CPU采用了段页式地址映射模型.进程代码中的地址为逻辑地址,经过段页式地址映射后,才真正访问物理内存. 段页式机制如下图.   Linux内核地址空间划分 通常32位Linux内核地址空间划分0~3G为用户空间,3~4G为内核空间.注意这里

Linux高端内存管理之永久内核映射

inux高端内存管理之永久内核映射 与直接映射的物理内存末端.高端内存的始端所对应的线性地址存放在high_memory变量中,在x86体系结构上,高于896MB的所有物理内存的范围大都是高端内存,它并不会永久地或自动地映射到内核地址空间,尽管x86处理器能够寻址物理RAM的范围达到4GB(启用PAE可以寻址到64GB).一旦这些页被分配,就必须in射到内核的逻辑地址空间上.在x86上,高端内存中的页被映射到3GB-4GB. 内核可以采用三种不同的机制将页框映射到高端内存:分别叫做永久内核映射.

linux 高端内存映射方式

1.1 内核地址空间(线性空间)分布   (1) 直接映射区:线性空间中从3G开始最大896M的区间,为直接内存映射区,该区域的线性地址和物理地址存在线性转换关系:线性地址=3G+物理地址. (2) 动态内存映射区:该区域由内核函数vmalloc来分配,特点是:线性空间连续,但是对应的物理空间不一定连续.vmalloc分配的线性地址所对应的物理页可能处于低端内存,也可能处于高端内存. (3) 永久内存映射区:该区域可访问高端内存.访问方法是使用alloc_page(_GFP_HIGHMEM)分配

Linux高端内存映射(上)【转】

转自:http://blog.csdn.net/vanbreaker/article/details/7579941 版权声明:本文为博主原创文章,未经博主允许不得转载.   目录(?)[-] 高端内存概述 永久内核映射   高端内存概述         在32位的系统上,内核占有从第3GB~第4GB的线性地址空间,共1GB大小,内核将其中的前896MB与物理内存的0~896MB进行直接映射,即线性映射,将剩余的128M线性地址空间作为访问高于896M的内存的一个窗口.引入高端内存映射这样一个概

Linux内存管理:高端内存的映射方式

高端内存是指物理地址大于 896M 的内存. 对于这样的内存,无法在"内核直接映射空间"进行映射. 为什么? 因为"内核直接映射空间"最多只能从 3G 到 4G,只能直接映射 1G 物理内存,对于大于 1G 的物理内存,无能为力. 实际上,"内核直接映射空间"也达不到 1G, 还得留点线性空间给"内核动态映射空间" 呢. 因此,Linux 规定"内核直接映射空间" 最多映射 896M 物理内存. 对于高端内

解析Linux系统下的高端内存

  Linux内核地址空间划分 通常32位Linux内核虚拟地址空间划分0~3G为用户空间,3~4G为内核空间(注意,内核可以使用的线性地址只有1G).注意这里是32位内核地址空间划分,64位内核地址空间划分是不同的. 通常32位Linux内核虚拟地址空间划分0~3G为用户空间,3~4G为内核空间(注意,内核可以使用的线性地址只有1G).注意这里是32位内核地址空间划分,64位内核地址空间划分是不同的. Linux内核高端内存的由来 当内核模块代码或线程访问内存时,代码中的内存地址都为逻辑地址,

Linux内存管理-高端内存(一)

高端内存是指物理地址大于 896M 的内存.对于这样的内存,无法在"内核直接映射空间"进行映射. 为什么? 因为"内核直接映射空间"最多只能从 3G 到 4G,只能直接映射 1G 物理内存,对于大于 1G 的物理内存,无能为力. 实际上,"内核直接映射空间"也达不到 1G, 还得留点线性空间给"内核动态映射空间" 呢. 因此,Linux 规定"内核直接映射空间" 最多映射 896M 物理内存. 对于高端内存

linux-3.2.36内核启动1-启动参数(arm平台 启动参数的获取和处理,分析setup_arch)【转】

转自:http://blog.csdn.net/tommy_wxie/article/details/17093297 最近公司要求调试一个内核,启动时有问题,所以就花了一点时间看看内核启动. 看的过程中总结了一点东西,希望可以帮助大家调试内核. 当我开始看的时候,第一件事是从网上搜集资料,不看不知道,一看吓一跳!牛人太多了,像这种内核启动的上古代码早就被人分析的彻彻底底.这注定我写的只能是烂微博了. 为了此微博有存在的必要,我会显示内核启动打印的代码位置(用绿色表示)及出现错误打印的原因(用红

Linux用户空间与内核空间数据传递

转载 - Linux用户空间与内核空间数据传递 作者 digoal 日期 2016-11-20 标签 Linux , 用户空间 , 内核空间 , 用户态 , 内核态 , 进程上下文 , 中断上下文 背景 原文 http://wdqfirst.blog.163.com/blog/static/113347411201182222252954/ 原文 Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针传递数据,因为Linux使用的虚拟内存机制,用户空间的数据