Linux 内核高-低端内存设置代码跟踪(ARM构架)

对于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(内核启动参数字符串)中的数据,

* 其中就分析了[email protected]参数初始化了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();函数可以解析内核启动参数中的许多字符串,但是对于我们这次分析内存的话主要是分析以下两个参数:

[email protected]参数,她为初始化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构架中,高-低段内存是内核通过内核启动参数( [email protected]和vmalloc=size)来自动配置的,如果没有特殊去配置他,那么在普通的ARM系统中是不会有高端内存存在的。除非你系统的RAM很大或vmalloc配置得很大,就很可能出现高端内存。

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

时间: 2024-10-25 16:50:35

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

Linux内核高端内存 转

Linux内核地址映射模型x86 CPU采用了段页式地址映射模型.进程代码中的地址为逻辑地址,经过段页式地址映射后,才真正访问物理内存. 段页式机制如下图.   Linux内核地址空间划分 通常32位Linux内核地址空间划分0~3G为用户空间,3~4G为内核空间.注意这里是32位内核地址空间划分,64位内核地址空间划分是不同的.   Linux内核高端内存的由来 当内核模块代码或线程访问内存时,代码中的内存地址都为逻辑地址,而对应到真正的物理内存地址,需要地址一对一的映射,如逻辑地址0xc00

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

转自:Linux用户空间与内核空间(理解高端内存) 参考: 1. 进程内核栈.用户栈 2. 解惑-Linux内核空间 3. linux kernel学习笔记-5 内存管理 Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针传递数据,因为Linux使用的虚拟内存机制,用户空间的数据可能被换出,当内核空间使用用户空间指针时,对应的数据可能不在内存中. Linux内核地址映射模型 x86 CPU采用了段页式地址映射模型.进程代码中的地址为逻辑地址,经过段页式地

linux内核探索之内存管理(二):linux系统中的内存组织--结点、内存域和页帧

本文主要参考<深入linux内核架构>(3.2节)及Linux3.18.3内核源码 概述:本文主要描述了内存管理相关的数据结构:结点pg_data_t.内存域struct zone以及页帧(物理页):struct page ,以及该结构相关的一些基本概念. 1. 概述 内存划分为接点,每个结点关联到系统中的一个处理器,在内核中表示为pg_data_t. 各个结点又划分为内存域,比如DMA内存域,高端内存域,普通内存域. 内核内存域的宏: enum zone_type { #ifdef CONF

Linux 内核子系统之内存与进程管理子系统

Linux 内核子系统的组成 由以上7个子系统组成. 一.内存管理子系统 职能: 1.从虚拟地址到物理地址映射的管理. 2.物理内存分配的管理. 地址映射管理 2.1虚拟地址空间分布 在Linux中使用的是虚拟地址,但是在访问硬件的时候使用的是物理地址.比如程序中用malloc函数分配的都是虚拟地址,但是硬件在使用时用的是物理地址,这其中就有一个转化关系. 它所支持的虚拟地址空间是由硬件地址总线宽度决定的,比如32位地址总线支持4GB虚拟内存. 用户空间(0-3G):用户程序. 内核空间(3-4

Linux内核工程导论——内存管理(一)

Linux内存管理 概要 物理地址管理 很多小型操作系统,例如eCos,vxworks等嵌入式系统,程序中所采用的地址就是实际的物理地址.这里所说的物理地址是CPU所能见到的地址,至于这个地址如何映射到CPU的物理空间的,映射到哪里的,这取决于CPU的种类(例如mips或arm),一般是由硬件完成的.对于软件来说,启动时CPU就能看到一片物理地址.但是一般比嵌入式大一点的系统,刚启动时看到的已经映射到CPU空间的地址并不是全部的可用地址,需要用软件去想办法映射可用的物理存储资源到CPU地址空间.

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

转自:http://blog.csdn.net/Baiduluckyboy/article/details/9667933 内存管理,不用多说,言简意赅.在内核里分配内存还真不是件容易的事情,根本上是因为内核不能想用户空间那样奢侈的使用内存. 先来说说内存管理.内核把物理页作为内存管理的基本单位.尽管处理器的最小可寻址单位通常是字,但是,内存管理单元MMU通常以页为单位进行处理.因此,从虚拟内存的交代来看,页就是最小单位.内核用struct  page(linux/mm.h)结构表示系统中的每个

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

简介: 作为一个系统管理程序(hypervisor),Linux® 有几个创新,2.6.32 内核中一个有趣的变化是 KSM(Kernel Samepage Merging)  允许这个系统管理程序通过合并内存页面来增加并发虚拟机的数量.本文探索 KSM 背后的理念(比如存储去耦合).KSM 的实现.以及如何管理 KSM. 服务器虚拟化 虚拟化技术从上世纪 60 年代开始出现,经由 IBM® System/360® 大型机得以流行.50 年过后,虚拟化技术取得了跨越式发展,使得多个操作系统和应用

调整linux内核尽量用内存,而不用swap

线上一台服务器kswapd0占用大量的cpu资源,导致负载过高,什么是kswapd0? Linux uses kswapd for virtual memory management such that pages that havebeen recently accessed are kept in memory and less active pages are paged outto disk. (what is a page?)-Linux uses manages memory in

linux内核探索之内存管理(四):对页表和页表项的操作

接上一节,主要参考<深入Linux内核架构>(3.3节),即linux-3.18.3 1. 对PTE的操作 最后一级页表中的项不仅包含了指向页的内存位置的指针,还在上述的多于比特位包含了与页有关的附加信息.尽管这些数据是特定于CPU的,它们至少提供了有关页访问控制的一些信息.下列位在linux内核支持的大多数CPU中都可以找到. arch/x86/include/asm/pgtable_types.h #define _PAGE_BIT_PRESENT 0 /* is present */ #