对于ARM中内核如何在启动的时候设置高低端内存的分界线(也是逻辑地址与虚拟地址分界线(虚拟地址)减去那个固定的偏移),这里我稍微引导下(内核分析使用Linux-3.0):

首先定位设置内核虚拟地址起始位置(也就是内核逻辑地址末端+1的地址)的文件:init.c (arch\arm\mm),在这个文件中的void __init bootmem_init(void)函数如下

void __init bootmem_init(void)

{

unsigned long min, max_low, max_high;

max_low = max_high = 0;

find_limits(&min, &max_low, &max_high);

arm_bootmem_init(min, max_low);

/*

* 1118.www.qixoo.qixoo.com Sparsemem tries to allocate bootmem in memory_present(),

* so must be done after the fixed reservations

*/

arm_memory_present();

/*

* sparse_init() needs the bootmem allocator up and running.

*/

sparse_init();

/*

* Now free the memory - free_area_init_node needs

* the sparse mem_map arrays initialized by sparse_init()

* for memmap_init_zone(), otherwise all PFNs are invalid.

*/

arm_bootmem_free(min, max_low, max_high);

high_memory = __va(((phys_addr_t)max_low << PAGE_SHIFT) - 1) + 1;

/*

* This doesn't seem to be used by the Linux memory manager any

* more, but is used by ll_rw_block. If we can get rid of it, we

* also get rid of some of the stuff above as well.

*

* Note: max_low_pfn and max_pfn reflect the number of _pages_ in

* the system, not the maximum PFN.

*/

max_low_pfn = max_low - PHYS_PFN_OFFSET;

max_pfn = max_high - PHYS_PFN_OFFSET;

}

这个high_memory = __va(((phys_addr_t)max_low << PAGE_SHIFT) – 1) + 1;语句就是关键。从这里可以知道max_low就是高端内存的起始地址(物理地址)。那么这个max_low是如何得到的?其实看上面的代码可以推测出,他其实是在find_limits(&min, &max_low, &max_high);中(在同一个文件中)被设置的:

static void __init find_limits(unsigned long *min, unsigned long *max_low,

unsigned long *max_high)

{

struct meminfo *mi = &meminfo;

int i;

*min = -1UL;

*max_low = *max_high = 0;

for_each_bank (i, mi) {

struct membank *bank = &mi->bank[i];

unsigned long start, end;

start = bank_pfn_start(bank);

end = bank_pfn_end(bank);

if (*min > start)

*min = start;

if (*max_high < end)

*max_high = end;

if (bank->highmem)

continue;

if (*max_low < end)

*max_low = end;

}
}

这个函数的意思很明显:通过扫描struct meminfo *mi = &meminfo;(结构体meminfo的数组)中的所有信息,设置三个指针所指的变量:

min :内存物理地址起始
max_low :低端内存区物理地址末端
max_high :高端内存区物理地址末端

从上面可以看出,max_low和max_high所保存的地址不同就是由于bank->highmem造成的,它是内存bank被设为高端内存的依据:

“如果这个内存bank是高端内存(bank->highmem != 0),跳过max_low = end;语句,max_low和max_high将不同(结果实际上是max_high > max_low);
否则假设没有一个内存bank是高端内存(所有bank->highmem == 0)max_low和max_high必然一致(高端内存大小为0)”

当然要实现这个函数的功能,必须保证meminfo所指数组中的所有bank是按照地址数据从小到大排序好的哦~~。但是这个大家不用担心,后面会看到的:)

经过上面的跟踪,焦点集中到了全局变量(同一个文件中):

/*
* This keeps memory configuration data used by a couple memory
* initialization functions, as well as show_mem() for the skipping
* of holes in the memory map. It is populated by arm_add_memory().
*/
struct meminfo meminfo;

这个结构体的定义(setup.h (arch\arm\include\asm)):

/*

* Memory map description

*/
#define NR_BANKS 8 /*现在ARM最大只支持到8个bank哦~*/

struct membank {

phys_addr_t start;

unsigned long size;

unsigned int highmem; /*我们关心的变量*/

};

struct meminfo {

int nr_banks;

struct membank bank[NR_BANKS]; /*我们关心的数组*/

};

extern struct meminfo meminfo;

#define for_each_bank(iter,mi) \

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

#define bank_pfn_start(bank) __phys_to_pfn((bank)->start)

#define bank_pfn_end(bank) __phys_to_pfn((bank)->start + (bank)->size)

#define bank_pfn_size(bank) ((bank)->size >> PAGE_SHIFT)

#define bank_phys_start(bank) (bank)->start

#define bank_phys_end(bank) ((bank)->start + (bank)->size)

#define bank_phys_size(bank) (bank)->size

只要找到初始化这个全局变量并完成排序的地方,就可以知道高端内存是如何配置的了!!OK,明确目标,go on~~~

通过查找代码,我们可以在setup.c (arch\arm\kernel)这个文件中找到相关的代码。在系统启动早期会运行的函数(具体的顺序你可以自行分析下ARM内核的启动流程,以后我也会写下)中有这样一个函数:

void __init setup_arch(char **cmdline_p)

{

struct machine_desc *mdesc;

unwind_init();

setup_processor();

mdesc = setup_machine_fdt(__atags_pointer);

if (!mdesc)

mdesc = setup_machine_tags(machine_arch_type);

machine_desc = mdesc;

machine_name = mdesc->name;

if (mdesc->soft_reboot)

reboot_setup("s");

init_mm.start_code = (unsigned long) _text;

init_mm.end_code = (unsigned long) _etext;

init_mm.end_data = (unsigned long) _edata;

init_mm.brk = (unsigned long) _end;

/* 填充cmd_line以备后用,维护boot_command_line */

strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE); /* 拷贝boot_command_line中的数据到cmd_line */

*cmdline_p = cmd_line;

parse_early_param(); /* 分析boot_command_line(内核启动参数字符串)中的数据,

* 其中就分析了mem=size@start参数初始化了struct meminfo meminfo;

* 同时如果有vmalloc=size参数也会初始化 vmalloc_min */

sanity_check_meminfo(); /* 在此处设置struct meminfo meminfo中每个bank中的highmem变量,

* 通过vmalloc_min确定每个bank中的内存是否属于高端内存 */

arm_memblock_init(&meminfo, mdesc); /* 在此处排序按地址数据从小到大排序 */

paging_init(mdesc);

request_standard_resources(mdesc);

unflatten_device_tree();

#ifdef CONFIG_SMP

if (is_smp())

smp_init_cpus();

#endif

reserve_crashkernel();

cpu_init();

tcm_init();

#ifdef CONFIG_MULTI_IRQ_HANDLER

handle_arch_irq = mdesc->handle_irq;

#endif

#ifdef CONFIG_VT

#if defined(CONFIG_VGA_CONSOLE)

conswitchp = &vga_con;

#elif defined(CONFIG_DUMMY_CONSOLE)

conswitchp = &dummy_con;

#endif

#endif

early_trap_init();

if (mdesc->init_early)

mdesc->init_early();

}

在上面的注释中,我已经表明了重点和解析,下面我细化下:

(1)获取参数部分

通过parse_early_param();函数可以解析内核启动参数中的许多字符串,但是对于我们这次分析内存的话主要是分析以下两个参数:

mem=size@start参数,她为初始化struct meminfo meminfo;(我们一直关注的内存信息哦~)提供信息。具体的获取信息的函数(同样位于setup.c (arch\arm\kernel)):

vmalloc=size参数,她为初始化vmalloc_min(需要保留的内核虚拟地址空间大小,也就是这个内核虚拟地址空间中除去逻辑地址空间和必要的防止越界的保护空洞后最少要预留的地址空间)提供信息。具体的实现函数(位于mmu.c (arch\arm\mm)):

(2)在获得了必要的信息(初始化好struct meminfo meminfo和vmalloc_min)后,内核通过sanity_check_meminfo函数自动去通过vmalloc_min信息来初始化每个meminfo.bank[?]中的highmem成员。此过程中如果有必要,将可能会改变meminfo中的bank数组。处理函数位于mmu.c (arch\arm\mm):

(3)最后必须做的就是排序了,完成了这个工作就可以完全被我们上面提到的find_limits函数使用了,而这个工作就放在了接下来的arm_memblock_init(&meminfo, mdesc);中的一开头:

通过上面的分析,整个高低端内存是如何确定的基本就清晰了,这里总结一下:

ARM构架中,高-低段内存是内核通过内核启动参数( mem=size@start和vmalloc=size)来自动配置的,如果没有特殊去配置他,那么在普通的ARM系统中是不会有高端内存存在的。除非你系统的RAM很大或vmalloc配置得很大,就很可能出现高端内存。

以上是我对高-低端内存学习时跟踪代码的备忘,如果大家在其中发现什么不对的地方,欢迎拍砖、纠正~~谢谢~

Linux 内核高-低端内存设置代码跟踪(ARM构架)的更多相关文章

  1. Linux内核高端内存 转

        Linux内核地址映射模型x86 CPU采用了段页式地址映射模型.进程代码中的地址为逻辑地址,经过段页式地址映射后,才真正访问物理内存. 段页式机制如下图.   Linux内核地址空间划分 通 ...

  2. Linux内核高端内存

    Linux内核地址映射模型 x86 CPU采用了段页式地址映射模型.进程代码中的地址为逻辑地址,经过段页式地址映射后,才真正访问物理内存. 段页式机制如下图. Linux内核地址空间划分 通常32位L ...

  3. linux用户空间和内核空间(内核高端内存)_转

    转自:Linux用户空间与内核空间(理解高端内存) 参考: 1. 进程内核栈.用户栈 2. 解惑-Linux内核空间 3. linux kernel学习笔记-5 内存管理   Linux 操作系统和驱 ...

  4. KSM剖析——Linux 内核中的内存去耦合

    简介: 作为一个系统管理程序(hypervisor),Linux® 有几个创新,2.6.32 内核中一个有趣的变化是 KSM(Kernel Samepage Merging)  允许这个系统管理程序通 ...

  5. 初探Linux内核中的内存管理

    Linux内核设计与实现之内存管理的读书笔记 初探Linux内核管理 内核本身不像用户空间那样奢侈的使用内存; 内核不支持简单快捷的内存分配机制, 用户空间支持? 这种简单快捷的内存分配机制是什么呢? ...

  6. [转]linux内核分析笔记----内存管理

    转自:http://blog.csdn.net/Baiduluckyboy/article/details/9667933 内存管理,不用多说,言简意赅.在内核里分配内存还真不是件容易的事情,根本上是 ...

  7. 【转】一篇关于32位Linux内核使用大内存的文章——Hugemem Kernel Explained  &nb

    红旗DC系列Linux操作系统(x86平台)中带有四类核心: UP (支持单内核) SMP (支持多内核) hugemem Icc* (用intel C编译器编译的核心) 其中hugemem核心往往引 ...

  8. Linux内核分析之理解进程调度时机跟踪分析进程调度与进程切换的过程

    一.原理分析 1.调度时机 背景不同类型的进程有不同的调度需求第一种分类I/O-bond:频繁的进行I/O:通常会花费很多时间等待I/O操作的完成CPU-bound:计算密集型:需要大量的CPU时间进 ...

  9. linux内核分析之内存管理

    1.struct page /* Each physical page in the system has a struct page associated with * it to keep tra ...

随机推荐

  1. ASP.NET Web API(三):安全验证之使用摘要认证(digest authentication)

    在前一篇文章中,主要讨论了使用HTTP基本认证的方法,因为HTTP基本认证的方式决定了它在安全性方面存在很大的问题,所以接下来看看另一种验证的方式:digest authentication,即摘要认 ...

  2. sql 几点记录

      1       With子句 1.1     学习目标 掌握with子句用法,并且了解with子句能够提高查询效率的原因. 1.2     With子句要点 with子句的返回结果存到用户的临时表 ...

  3. Excel导入导出(篇二)

    <body> <h3>一.Excel导入</h3> <h5>.模板下载:<a href="UpFiles/TemplateFiles/学 ...

  4. nios II--实验7——数码管IP软件部分

    软件开发 首先,在硬件工程文件夹里面新建一个software的文件夹用于放置软件部分:打开toolsàNios II 11.0 Software Build Tools for Eclipse,需要进 ...

  5. 40-cut 简明笔记

    从输入行中选取字符或者字段 cut [options] [file-list] cut 从输入行中选取字符或者字段,并将他们写到标准输出,字符和字段从1开始编号 参数 file-list 是文件的路径 ...

  6. python基础_制作多级菜单_(运用:字典_列表_元组等知识)

    #!/usr/bin/env python # -*- coding:utf-8 -*- #Author: nulige db = {} path = {} while True: temp = db ...

  7. Beta版本冲刺Day3

    会议讨论: 628:已经将原本写在jsp中的所有界面修饰代码转移到了css文件中,同时当页面跳转的时候也不会出现崩溃的现象,并且已经解决了上次无法连接数据库的问题.但是又遇到了一些新的小问题,希望明天 ...

  8. oracle数据泵实现不同用户之间的导出导入

    来源于:http://www.cnblogs.com/kevinsun/archive/2007/02/03/638803.aspx http://blog.sina.com.cn/s/blog_68 ...

  9. Java 接口中常量的思考

    接口中不允许方法的实现,而抽象类是允许方法实现的及定义变量的,因此我们可以看出接口是比抽象类更高层次的抽象.如果接口可以定义变量,但是接口中的方法又都是抽象的,在接口中无法通过行为(例如set()方法 ...

  10. [转]jquery 对 Json 的各种遍历

    原文地址:http://caibaojian.com/jquery-each-json.html 概述 JSON(javascript Object Notation) 是一种轻量级的数据交换格式,采 ...