内存管理初始化源码1:setup_arch
源码声明:基于Linux kernel 3.08
1. 在kernel/arch/mips/kernel/head.S中会做一些特定硬件相关的初始化,然后会调用内核启动函数:start_kernel;
2. start_kernel是通用的内核启动函数,但是在初始化内核过程中,必然有一些参数是特定于硬件体系结构的,这些特定于硬件体系结构的设置通过调用函数setup_arch函数;
3. 我们看看MIPS架构的setup_arch函数做了哪些特定于MIPS的设置:
/* kernel/arch/mips/kernel/setup.c */
void __init setup_arch(char **cmdline_p)
{
cpu_probe();
prom_init(); #ifdef CONFIG_EARLY_PRINTK
setup_early_printk();
#endif
cpu_report();
check_bugs_early(); #if defined(CONFIG_VT)
#if defined(CONFIG_VGA_CONSOLE)
conswitchp = &vga_con;
#elif defined(CONFIG_DUMMY_CONSOLE)
conswitchp = &dummy_con;
#endif
#endif /* 这个是内存管理相关的,我们只关注该函数,其他的看看函数名,意淫一下就可以了 */
arch_mem_init(cmdline_p); resource_init();
plat_smp_setup();
}
4. arch_mem_init
/* 这么关键的注释,一定要仔细阅读 */
/*
* arch_mem_init - initialize memory management subsystem(初始化 memory management subsystem)
*
* o plat_mem_setup() detects the memory configuration and will record detected
* memory areas using add_memory_region.
* plat_mem_setup():探测memory配置,然后使用add_memory_region记录探测到的内存区域【低端内存,高端内存,DMA的区域】。
*
* At this stage the memory configuration of the system is known to the
* kernel but generic memory management system is still entirely uninitialized.
* 此时,kernel知道memory的配置,但是generic memory management sysetm依然没有被初始化。
*
* o bootmem_init()
* o sparse_init()
* o paging_init()
*
* At this stage the bootmem allocator is ready to use.
* 此时,bootmem allocator可以使用了。
*
* NOTE: historically plat_mem_setup did the entire platform initialization.
* This was rather impractical because it meant plat_mem_setup had to
* get away without any kind of memory allocator. To keep old code from
* breaking plat_setup was just renamed to plat_setup and a second platform
* initialization hook for anything else was introduced.
*
* NOTE:从历史上看,plat_mem_setup完成整个platform的初始化。
* 这是不切实际啊的,因为这意味着plat_mem_setup必须在不使用memory allocator时完成所有工作。......
*/ static int usermem __initdata; static int __init early_parse_mem(char *p)
{
unsigned long start, size; /* 该函数会被调用2次:
* 第一次:p 是 "224M@0x0"
* 第二次:p 是 "256M@0x30000000"
* early_parse_mem的参数是如何传递的需要深入了解两个函数:early_parm和parse_eraly_param,这个还是有些复杂,我们就不去深入了解了。但是我们可以大体猜测出这些函数的目的,就是:
* 从commandline中根据关键字"mem"提取物理内存的信息,即起始地址和大小。
* 这里我们看到该函数被调用两次,是由于我们的command line包含两个"mem"信息,这是由于我们的板子使用是4+8的EMCP,那么RAM大小是512M,刚好是两个256M。
* 第一个变成了224M,可能是由于RAM不是精确的512M,也可能是uboot占用了一部分。具体就不太了解了。
*/ /*
* If a user specifies memory size, we
* blow away any automatically generated
* size.
*/
if (usermem == ) {
boot_mem_map.nr_map = ;
usermem = ;
}
start = ;
size = memparse(p, &p);
if (*p == '@')
start = memparse(p + , &p); // 解析”mem“字符串,提取memory信息 add_memory_region(start, size, BOOT_MEM_RAM); // 记录memory信息,就是对boot_mem_map结构体的赋值,代码我就不粘了
return ;
}
early_param("mem", early_parse_mem); #ifdef CONFIG_ANDROID_PMEM
#ifdef CONFIG_SOC_4775
//unsigned long start, size;
static unsigned long g_pmem_total_size=;
static unsigned long g_pmem_start=; /* called in arch/mips/xburst/soc-4775/common/setup.c */
unsigned long set_reserved_pmem_total_size(unsigned long size)
{
g_pmem_total_size = size;
return ;
} unsigned long get_reserved_pmem_size(void)
{
return g_pmem_total_size;
} unsigned long get_reserved_pmem_start(void)
{
return g_pmem_start;
} #endif /* CONFIG_SOC_4775 */
#endif /* CONFIG_ANDROID_PMEM */ static void __init arch_mem_init(char **cmdline_p)
{
extern void plat_mem_setup(void); /* call board setup routine */
plat_mem_setup(); pr_info("Determined physical RAM map:\n"); strlcpy(boot_command_line, arcs_cmdline, COMMAND_LINE_SIZE);
strlcpy(command_line, boot_command_line, COMMAND_LINE_SIZE);
// arcs_cmdline : console=tty3,115200n8 mem=224M@0x0 mem=256M@0x30000000 ip=off root=/dev/ram0 rw rdinit=/init rd_start=0x81A00000 rd_size=0x0012E720
// 这个command line是由uboot传递过来的一些启动参数,具体如何过来的不再深入追了,最后kernel会从这里提取内存信息,串口信息,其他信息(不懂)
*cmdline_p = command_line;
parse_early_param(); // 内核一种看起来很牛逼的函数调用,具体流程忽略,最后结果就是调到了:early_parse_mem
if (usermem) {
pr_info("User-defined physical RAM map:\n");
print_memory_map(); // 打印内存信息,如下
/*
memory: 0e000000 @ 00000000 (usable)
memory: 10000000 @ 30000000 (usable)
第一个字段是是size,第二个字段是起始地址
*/
bootmem_init();
device_tree_init();
sparse_init();
plat_swiotlb_setup();
paging_init();
}
5. plat_mem_setup
该函数是针对每种不同的CPU进行的配置。/* kernel/arch/mips/xburst/soc-m200/common/setup.c *//* 针对m200这种mips架构的CPU,进行的配置 */
void __init plat_mem_setup(void)
{
/* jz mips cpu special */
__asm__ (
"li $2, 0xa9000000 \n\t"
"mtc0 $2, $5, 4 \n\t"
"nop \n\t"
::"r"()); /* use IO_BASE, so that we can use phy addr on hard manual
* directly with in(bwlq)/out(bwlq) in io.h.
*/
set_io_port_base(IO_BASE); // kernel/arch/mips/include/asm/mach-generic/spaces.h: #define IO_BASE _AC(0xa0000000, UL) /*
kernel/kernel/resouce.c
struct resource ioport_resource = {
.name = "PCI_IO",
.start = 0,
.end = IO_SPACE_LIMIT,
.flags = IORESOURCE_IO,
};
EXPORT_SYMBOL(ioport_resource);
*/
// 记录 io resource
ioport_resource.start = 0x00000000;
ioport_resource.end = 0xffffffff;
iomem_resource.start = 0x00000000;
iomem_resource.end = 0xffffffff;
setup_init(); // 一些硬件相关的初始化
init_all_clk(); // 初始化所有的始终。如:CCLK:1200MHZ L2CLK:300MHZ H0CLK:200MHZ H2CLK:200MHz PCLK:100MHZ #ifdef CONFIG_ANDROID_PMEM
/* reserve memory for pmem. */
board_pmem_setup();
#endif
return;
}
set_io_port_base:
/*
* Gcc will generate code to load the value of mips_io_port_base after each
* function call which may be fairly wasteful in some cases. So we don't
* play quite by the book. We tell gcc mips_io_port_base is a long variable
* which solves the code generation issue. Now we need to violate the
* aliasing rules a little to make initialization possible and finally we
* will need the barrier() to fight side effects of the aliasing chat.
* This trickery will eventually collapse under gcc's optimizer. Oh well.
*/
/*
* GCC会在每个函数的调用之后,产生Code来加载mips_io_port_base的值,这有时完全是浪费的。因此,我们不完全按照手册。我们告诉gcc,mips_io_port_base是一个长整型变量,这将会解决code generation问题。
* 现在,我们需要违反aliasing(别名)规则,来完成初始化,最后我们需要barrier()来fight side effects of the aliasing chat.
* 这种欺诈行为最终将会随着gcc的优化而崩塌。Oh well。
*/
static inline void set_io_port_base(unsigned long base)
{
* (unsigned long *) &mips_io_port_base = base; // 简单地赋值语句
barrier(); // 内存屏障,下边我们简单看看
}
/* kernel/include/linux/compiler-gcc.h */ /* Optimization barrier */
/* The "volatile" is due to gcc bugs */
#define barrier() __asm__ __volatile__("": : :"memory") /*
* 1. __asm__用于指示编译器在此插入汇编语句
* 2. __volatile__用于告诉编译器,严禁将此处的汇编语句与其它的语句重组合优化。即:原原本本按原来的样子处理这这里的汇编。
* 3. memory强制gcc编译器假设RAM所有内存单元均被汇编指令修改,这样cpu中的registers和cache中已缓存的内存单元中的数据将作废。cpu将不得不在需要的时候重新读取内存中的数据。这就阻止了cpu又将registers,cache中的数据用于去优化指令,而避免去访问内存。
* 4. "":::表示这是个空指令。
*/
6. print_memory_map
再次回顾我们的主线:start_kernel—>setup_arch—>arch_mem_init。
刚才我们分析了arch_mem_init调用的第一个函数:plat_mem_setup。
该函数就是和具体的CPU相关,该函数做了这么几件事:1. set_io_port_base: mips_io_port_base = 0xa0000000.
2. 记录io resource.
3. 一些硬件初始化和始终初始化
接着,我们继续看arch_mem_init调用的第二个函数:print_memory_map。
很明显,这个函数是打印当前的内存信息,但是我们也详细去看看,因为内部有很重要的数据结构。
static void __init print_memory_map(void)
{
int i;
const int field = * sizeof(unsigned long); for (i = ; i < boot_mem_map.nr_map; i++) { // boot_mem_map:是一个类型为struct boot_mem_map的全局变量
printk(KERN_INFO " memory: %0*Lx @ %0*Lx ",
field, (unsigned long long) boot_mem_map.map[i].size,
field, (unsigned long long) boot_mem_map.map[i].addr); switch (boot_mem_map.map[i].type) {
case BOOT_MEM_RAM:
printk(KERN_CONT "(usable)\n");
break;
case BOOT_MEM_ROM_DATA:
printk(KERN_CONT "(ROM data)\n");
break;
case BOOT_MEM_RESERVED:
printk(KERN_CONT "(reserved)\n");
break;
default:
printk(KERN_CONT "type %lu\n", boot_mem_map.map[i].type);
break;
}
}
}
没错,我们说的重要的数据结构就是:boot_mem_map.
/* kernel/arch/mips/include/asm/bootinfo.h */
/*
* A memory map that's built upon what was determined
* or specified on the command line.
*/
struct boot_mem_map {
int nr_map;
struct boot_mem_map_entry {
phys_t addr; /* start of memory segment */
phys_t size; /* size of memory segment */
long type; /* type of memory segment */
} map[BOOT_MEM_MAP_MAX];
};
7. 在arch_mem_init我们还有bootmem_init之后的几个调用,具体分析见下篇文章
内存管理初始化源码1:setup_arch的更多相关文章
- 内存管理初始化源码2:setup_arch
PFN相关宏说明: /* kernel/include/linux/pfn.h */ PFN : Page Frame Number(物理页帧) /* * PFN_ALIGN:返回地址x所在那一页帧的 ...
- 内存管理初始化源码4:add_active_range
我们在阅读源码时,函数功能可以分为两类:1. bootmem.c 2. page_alloc.c. 1. bootmem.c是关于bootmem allocator的,上篇文章已经简述过. 2. pa ...
- 内存管理初始化源码3:bootmem
start_kernel ——> setup_arch ——> arch_mem_init ——> bootmem_init ——> init_bootmem_node: 此时 ...
- 内存管理初始化源码5:free_area_init_nodes
start_kernel ——> setup_arch ——> arch_mem_init ——> |——> bootmem_init |——> device_tree ...
- C++动态内存管理与源码剖析
引言 在本篇文章中,我们主要剖析c++中的动态内存管理,包括malloc.new expression.operator new.array new和allocator内存分配方法以及对应的内存释放方 ...
- Jedis cluster集群初始化源码剖析
Jedis cluster集群初始化源码剖析 环境 jar版本: spring-data-redis-1.8.4-RELEASE.jar.jedis-2.9.0.jar 测试环境: Redis 3.2 ...
- C#共享内存实例 附源码
原文 C#共享内存实例 附源码 网上有C#共享内存类,不过功能太简单了,并且写内存每次都从开头写.故对此进行了改进,并做了个小例子,供需要的人参考. 主要改进点: 通过利用共享内存的一部分空间(以下称 ...
- 内存管理 初始化(八) 至kswapd_init
至此,内存初始化部分已看完,遗留问题: 1.对于unicore或者mips的页表建立都很清楚,但是对于ARM我不清楚: 初始化部分涉及的页表映射建立,我都以unicore架构为准,ARM的页表映射从原 ...
- Firefly卡牌手游《暗黑世界V1.5》服务器端源码+GM管理后台源码
http://www.9miao.com/content-6-304.html Firefly卡牌手游<暗黑世界V1.5>服务器端源码+GM管理后台源码 关于<暗黑世界V1.5> ...
随机推荐
- centos go 安装 使用
#goland 确保能ping通百度[root@z my_project]# vi /etc/resolv.conf# Generated by NetworkManagersearch locald ...
- Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.util.List com.catherine.forrealm.about_utils.RealmHelper.findAllStudent()' on a null object reference
报错: 解决方法: private RealmHelper realm_search = new RealmHelper(); 进而发现在写RecyclerView时,遗漏如下代码: recy_sea ...
- 使用 gopacket 进行数据包捕获,注入和分析
原文链接:https://www.devdungeon.com/content/packet-capture-injection-and-analysis-gopacket 接口文档:https:// ...
- 结对项目 实现自动生成四则运算题目的程序 (C++)
本次作业由 陈余 与 郭奕材 结对完成 零.github地址: https://github.com/King-Authur/-Automatically-generate-four-arithmet ...
- 10款人气暴涨的PHP开源工具
若想创建动态而又新颖的Web应用程序,PHP便是理想的选择.不用说,在Web开发世界里,PHP是最流行的语言之一.一些非常好用的PHP开源工具着实拯救了不少开发任务繁重的PHP开发人员,减轻他们的开发 ...
- lammps_data文件
一.notes: 1.不在data文件里写“#”(注释),否则,容易出错: 2.前两行不用写东西(建议): 3.相互作用系数可以不用写在data里边(如pair_coeff等),可有可无,but fo ...
- Linux两台服务器mysql数据库同步
我们在做web系统部署的时候往往涉及到两台甚至多台数据库的备份,为了数据安全考虑(虽然说到底不过是一堆0 1,但是价值千金啊),所以我们还是乖乖做同步把! 1.准备两台Linux服务器(主.从) 2. ...
- py_冒泡排序
import random """ 排序:将一组无序记录序列调整为有序记录序列 列表排序:将无序列表调整为有序列表 输入:列表 输出:有序列表 升序与降序 内置函数sor ...
- 现在的市场对 C++ 的需求大吗?
分享 大师助手 先说结论:需求还是很大,但是没有什么初级程序员能干的岗位. 游戏引擎,存储,推荐引擎,infra,各种各样的性能敏感场景.这些都是C++的刚需场景,别的语言基本替代不了的.除了pin ...
- MySQL常用指令,java,php程序员,数据库工程师必备。程序员小冰常用资料整理
MySQL常用指令,java,php程序员,数据库工程师必备.程序员小冰常用资料整理 MySQL常用指令(备查) 最常用的显示命令: 1.显示数据库列表. show databases; 2.显示库中 ...