在实际的工作中,由于产品型号的不同,经常需要调整linux所管理的内存的大小,而内核在启动阶段,会两次去解析从uboot传递过来的关于内存的信息,具体如下:

一、解析从uboot传递过来的tag(在parse_tags中处理)

在uboot的do_bootm_linux()函数中,会创建一系列需要传递给内核的tag,所有的tag以链表形式链接到指定的物理内存中。setup_start_tag用来建立起始的tag,而起始的物理地址由bd->bi_boot_params指定:

  1. static void setup_start_tag (bd_t *bd)
  2. {
  3. params = (struct tag *) bd->bi_boot_params;
  4.  
  5. params->hdr.tag = ATAG_CORE;
  6. params->hdr.size = tag_size (tag_core);
  7.  
  8. params->u.core.flags = 0;
  9. params->u.core.pagesize = 0;
  10. params->u.core.rootdev = 0;
  11.  
  12. params = tag_next (params);
  13. }

bi_boot_params是在board_init中初始化的,此地址是与内核协商一致的用来存放tag的基址。

int board_init (void)

{

…………

gd->bd->bi_boot_params = CFG_BOOT_PARAMS;

…………

}

而内存的tag是在setup_memory_tags()函数中创建的,其hdr.tag指定了tag的类型为ATAG_MEM

  1. static int __init parse_tag_mem32(const struct tag *tag)
  2. {
  3. if (meminfo.nr_banks >= NR_BANKS) {
  4. printk(KERN_WARNING
  5. "Ignoring memory bank 0x%08x size %dKB\n",
  6. tag->u.mem.start, tag->u.mem.size / 1024);
  7. return -EINVAL;
  8. }
  9. arm_add_memory(tag->u.mem.start, tag->u.mem.size);
  10. return 0;
  11. }
  12.  
  13. __tagtable(ATAG_MEM, parse_tag_mem32);

在内核中,会通过__tagtable 宏来建立起相关的struct tagtable 的数据结构,并放入".taglist.init" 段中,

#define __tag __used __attribute__((__section__(".taglist.init")))

#define __tagtable(tag, fn) \

static struct tagtable __tagtable_##fn __tag = { tag, fn }

  1. static int __init parse_tag_mem32(const struct tag *tag)
  2. {
  3. return arm_add_memory(tag->u.mem.start, tag->u.mem.size);
  4. }
  5.  
  6. __tagtable(ATAG_MEM, parse_tag_mem32);

而在start_kernel()->setup_arch()->parse_tags()函数中会根据从指定的物理内存中解析出来的tag的类型(即在uboot中写入的hdr.tag)去解析不同的tag。

在内核中此物理内存地址是在MACHINE_START中定义的,其中的boot_params与uboot中的bi_boot_params数据段指向相同的物理内存地址。因此是在uboot中写入tag,在内核中此地址解析tag。

  1. MACHINE_START(hi3520v100, "hi3520v100")
  2. .phys_io = IO_SPACE_PHYS_START,
  3. .io_pg_offst = (IO_ADDRESS(IO_SPACE_PHYS_START) >> 18) & 0xfffc,
  4. .boot_params = PHYS_OFFSET + 0x100,
  5. .map_io = hisilicon_map_io,
  6. .init_irq = hisilicon_init_irq,
  7. .timer = &hisilicon_timer,
  8. .init_machine = hisilicon_init_machine,
  9. MACHINE_END

  1. struct tagtable {
  2. __u32 tag;
  3. int (*parse)(const struct tag *);
  4. };

在parse_tags()中,会根据读出来的tag的类型,即hdr.tag与从".taglist.init"段中的struct tagtable中的tag字段比较,如果相等,便执行struct tagtable中的parse()函数,对内存的tag来讲,其类型是ATAG_MEM,解析函数是parse_tag_mem32();

  1. static int __init parse_tag_mem32(const struct tag *tag)
  2. {
  3. if (meminfo.nr_banks >= NR_BANKS) {
  4. printk(KERN_WARNING
  5. "Ignoring memory bank 0x%08x size %dKB\n",
  6. tag->u.mem.start, tag->u.mem.size / 1024);
  7. return -EINVAL;
  8. }
  9. arm_add_memory(tag->u.mem.start, tag->u.mem.size);
  10. return 0;
  11. }
  12.  
  13. __tagtable(ATAG_MEM, parse_tag_mem32);

在内核中,物理内存的起始地址和大小存放在一个struct meminfo meminfo的全局变量中:

  1. struct meminfo {
  2. int nr_banks;
  3. struct membank bank[NR_BANKS];
  4. };
  1. struct membank {
  2. unsigned long start;
  3. unsigned long size;
  4. int node;
  5. };

nr_banks表示内核总共管理了多少个bank。

struct membank记录了内核中各个bank的信息,start表示起始地址,size表示此bank的大小,node表示此bank属于哪个内存结点。

Linux内核可以管理多个不连续的物理内存,每段连续的物理内存的大小和起始地址存在一个struct membank结构体中,有多少段物理内存,就有多少个bank。

parse_tag_mem32解析在uboot中建立的关于内存的tag,把其中的物理内存地址和大小填充到bank中。

二、解析从uboot传递过来的boot_command_line(在parse_cmdline函数中解析)。

boot_command_line命令行是在uboot的fix_bootargs()函数里建立的。即在uboot中看到的bootargs的环境变量

  1. static void fix_bootargs(char *cmdline)
  2. {
  3. ….……
  4. /* fix "mem=" params */
  5. p = strstr(cmdline,"mem=");
  6. if(!p) {
  7. sprintf(args," mem=%dM",gd->bd->bi_dram[0].size/0x200000);
  8. strcat(cmdline,args);
  9. }
  10. ……………
  11. }

在内核中是通过early_mem()来解析boot_command_line中有关内存大小的参数行的。

  1. static void __init early_mem(char **p)
  2. {
  3. static int usermem __initdata = 0;
  4. unsigned long size, start;
  5.  
  6. /*
  7. * If the user specifies memory size, we
  8. * blow away any automatically generated
  9. * size.
  10. */
  11. if (usermem == 0) {
  12. usermem = 1;
  13. meminfo.nr_banks = 0;
  14. }
  15.  
  16. start = PHYS_OFFSET;
  17. size = memparse(*p, p);
  18. if (**p == '@')
  19. start = memparse(*p + 1, p);
  20.  
  21. arm_add_memory(start, size);
  22. }
  23. __early_param("mem=", early_mem);

该函数解析从uboot传递进来的boot_command_line命令行参数中以“mem=”开头的命令行,如果boot_command_line中有以“mem=”开头的命令行,就调用该函数解析“mem=”之后的关于内存的信息,

把内存的大小写到对应的bank中去,内存的基地址在此处是一个默认值。如果有两段不连续的物理内存,可以在boot_command_line中设置如下内容即可:

mem=72M@0xe2000000 mem=128M@0xe8000000

在此处,定义了static int usermem __initdata = 0,从而设置meminfo.nr_banks = 0,这样把前面解析uboot的tag所赋值的bank内容又重写了,所以相当于前面解析tag的操作没有生效,起作用的还是此处的解析boot_command_line的操作。

三、以上是内核启动过程中所做的两次解析内存参数的操作,在实际应用中需要修改linux内存大小时可以采取相应的方法:

1、 修改uboot中内存相关的tags或者bootargs的命令行参数。这种做法虽然可以修改linux管理的内存的大小,但是由于要修改uboot,这样会对产品生产中增加困难,而且bootloader在原则上是要尽量少做改动,防止由于修改bootloader造成板子无法启动等问题,所以此方法不推荐使用。

2、 通过在解析boot_command_line之前修改其中的”mem=”之后的相关内容来修改linux所管理的内存大小,这样可以做到不同产品间的兼容性,而且后续的产品升级等方面也比较简单容易操作。

在海思平台上实现了这种做法。

  1. void __init hikio_fix_meminfo(char *cmdline,struct meminfo *mi)
  2. {
  3. ……………….
  4. strcpy(p,p+8);
  5. strcat(cmdline," mem=72M");
  6. mi->bank[0].size = 72*0x100000;
  7. }

然后在解析cmdline之前执行此函数。
hikio_fix_meminfo(from,&meminfo);/* wangqian fix for cost-down boards*/

memcpy(boot_command_line, from, COMMAND_LINE_SIZE);

boot_command_line[COMMAND_LINE_SIZE-1] = '\0';

parse_cmdline(cmdline_p, from);

这种方法可以很方便的根据不同的产品型号修改内存的大小,而且只需要修改内核部分,不用去对uboot进行改动,所以是最方便快捷的方式。

from:http://sunjiangang.blog.chinaunix.net/uid-9543173-id-3571668.html

linux下内存大小、起始地址的解析与修改的更多相关文章

  1. Linux下逻辑地址、线性地址、物理地址详细总结

    Linux下逻辑地址.线性地址.物理地址详细总结 一.逻辑地址转线性地址      机器语言指令中出现的内存地址,都是逻辑地址,需要转换成线性地址,再经过MMU(CPU中的内存管理单元)转换成物理地址 ...

  2. linux下内存

    MMU由一个或一组芯片组成.其功能是把逻辑地址映射为物理地址,进行地址转换(MMU是CPU的一部分) 机器指令仍然用逻辑地址指定一个操作数的地址或一条指令的地址 每个逻辑地址都由一个段选择符(16位) ...

  3. [转帖]Linux下逻辑地址、线性地址、物理地址详细总结

    Linux下逻辑地址.线性地址.物理地址详细总结 https://www.cnblogs.com/alantu2018/p/9002441.html 总结的挺好的 现在应该是段页式管理 使用MMU和T ...

  4. Linux下内存查看命令

    在Linux下面,我们常用top命令来查看系统进程,top也能显示系统内存.我们常用的Linux下查看内容的专用工具是free命令. Linux下内存查看命令free详解: 在Linux下查看内存我们 ...

  5. Linux下内存查看及详解

    在Linux下面,我们常用top命令来查看系统进程,top也能显示系统内存.我们常用的Linux下查看内容的专用工具是free命令. Linux下内存查看命令free详解: 在Linux下查看内存我们 ...

  6. 大并发连接的oracle在Linux下内存不足的问题的分析

    大并发连接的oracle在Linux下内存不足的问题的分析 2010-01-28 20:06:21 分类: Oracle 最近一台装有Rhel5.3的40G内存的机器上有一个oracle数据库,数据库 ...

  7. Linux下内存映射文件的用法简介

    由于项目需要,所以学习了一下Linux下内存映射文件的用法,在这里共享一下自己的收获,希望大家提出宝贵意见,进行交流. 简介: 内存映射文件与虚拟内存有些类似,通过内存映射文件可以保留一个地址空间的区 ...

  8. Linux下的ELF可执行文件的格式解析 (转)

    LInux命令只是和Kernel一起被编译进操作系统的存在于FS的ELF格式二进制文件,或者权限足够的脚本,或者一个软链 ELF(Executable and Linking Format)是一种对象 ...

  9. linux下内存的统计和内存泄露类问题的定位

    在产品的开发中,通过对当前系统消耗内存总量的统计,可以对产品所需内存总量进行精确的评估,从而选择合适的内存芯片与大小,降低产品的成本.在遇到内存泄露类问题时,经常会对此束手无策,本文通过对proc下进 ...

随机推荐

  1. 关于web XSS注入问题

    对web安全方面的知识非常薄弱,这篇文章把Xss跨站攻击和sql注入的相关知识整理了下,附带公司写的一个filer. 对于防止sql注入发生,我只用过简单拼接字符串的注入及参数化查询,可以说没什么好经 ...

  2. [LeetCode] Count Binary Substrings 统计二进制子字符串

    Give a string s, count the number of non-empty (contiguous) substrings that have the same number of ...

  3. volatile 到i++ 原子操作 详解

    1.可见性(Visibility) 可见性是指,当一个线程修改了某一个全局共享变量的数值,其他线程是否能够知道这个修改. 显然,在串行程序来说可见性的问题是不存在的.因为你在任何一个地方操作修改了某个 ...

  4. js switch判断 三目运算 while 及 属性操作

    三 目运算:如var a = 10: var b= 12: c = a>b ?a:b; 若成立执行a否则执行b var isHide = true; 若用if判断语句如下 if(isHide) ...

  5. 我的webstorm 使用总结

    有一次 ,我改了公共组件里的好多组件 ,然后我肯定是哪里改的不对 ,出语法错误了 ,但是我的 webstorm 编译并没有报错,然后我就手动一点点 改 ,去看 ,最后还是没有找到 ,我就把文件夹给删了 ...

  6. Kylin系列之二:原理介绍

    Kylin系列之二:原理介绍 2018年4月15日 15:52 因何而生 Kylin和hive的区别 1. hive主要是离线分析平台,适用于已经有成熟的报表体系,每天只要定时运行即可. 2. Kyl ...

  7. [ZJOI 2007]Hide 捉迷藏

    Description 捉迷藏 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩捉迷藏游戏.他们的家很大且构造很奇特,由N个屋子和N-1条双 ...

  8. [ZJOI2010]排列计数

    题目描述 称一个1,2,...,N的排列P1,P2...,Pn是Magic的,当且仅当2<=i<=N时,Pi>Pi/2. 计算1,2,...N的排列中有多少是Magic的,答案可能很 ...

  9. NOIP 2008 双栈排序

    题目描述 Tom最近在研究一个有趣的排序问题.如图所示,通过2个栈S1和S2,Tom希望借助以下4种操作实现将输入序列升序排序. 操作a 如果输入序列不为空,将第一个元素压入栈S1 操作b 如果栈S1 ...

  10. 「LibreOJ NOIP Round #1」旅游路线

    Description T 城是一个旅游城市,具有 nnn 个景点和 mmm 条道路,所有景点编号为 1,2,...,n1,2,...,n1,2,...,n.每条道路连接这 nnn 个景区中的某两个景 ...