对于一个这样的参数 mem=151M mem=118M@768M 内核中是怎么处理的已经在前篇文章里提到。这里再详细分析一下由这三个数据而引发的一连串的计算。使用这个参数的内核在启动过程中打印的内存信息:
[ 0.000000] Determined physical RAM map: [ 0.000000] memory: 04000000 @ 00000000 (usable) [ 0.000000] User-defined physical RAM map: [ 0.000000] memory: 09700000 @ 00000000 (usable) [ 0.000000] memory: 07600000 @ 30000000 (usable) [ 0.000000] Zone PFN ranges: [ 0.000000] Normal 0x00000000 -> 0x00020000 [ 0.000000] HighMem 0x00020000 -> 0x00037600 [ 0.000000] Movable zone start PFN for each node [ 0.000000] early_node_map[2] active PFN ranges [ 0.000000] 0: 0x00000000 -> 0x00009700 [ 0.000000] 0: 0x00030000 -> 0x00037600 [ 0.000000] On node 0 totalpages: 68864 [ 0.000000] Normal zone: 1152 pages used for memmap [ 0.000000] Normal zone: 0 pages reserved [ 0.000000] Normal zone: 37504 pages, LIFO batch:7 [ 0.000000] Initialising map node 0 zone 0 pfns 0 -> 131072 [ 0.000000] HighMem zone: 842 pages used for memmap [ 0.000000] HighMem zone: 29366 pages, LIFO batch:7 [ 0.000000] Initialising map node 0 zone 1 pfns 131072 -> 226816 [ 0.000000] Built 1 zonelists in Zone order, mobility grouping on. Total pages: 66870 ………… [ 0.092000] Memory: 266060k/154624k available (3044k kernel code, 9152k reserved, 1166k data, 152k init, 120832k highmem)
这里的“Determined physical RAM map:”和“User-defined physical RAM map:”便是由arch_mem_init函数根据boot_mem_map里面的信息打印的。前者是自动检测的,后者是启动参数指定的。并且前者会被后者覆盖,除非启动参数中不指定mem参数。下面对这一大堆数据慢慢地做做计算, 看看它们到底是怎么得来的。
[ 0.000000] Zone PFN ranges: [ 0.000000] Normal 0x00000000 -> 0x00020000 [ 0.000000] HighMem 0x00020000 -> 0x00037600 [ 0.000000] Movable zone start PFN for each node [ 0.000000] early_node_map[2] active PFN ranges [ 0.000000] 0: 0x00000000 -> 0x00009700 [ 0.000000] 0: 0x00030000 -> 0x00037600
代码位置:head.S --> start_kernel --> setup_arch --> arch_mem_init --> paging_init --> free_area_init_nodes
这一段log就是由free_area_init_nodes函数打印的。
Zone PFN ranges打印的是每个Zone最小和最大可能的PFN取值。这只是一个允许的取值范围,并不一定是真正用到的PFN值。真正使用的PFN肯定是该范围的一个子集。我们的系统中配置有两个Zone分别是Normal和HighMem.
[ 0.000000] Normal 0x00000000 -> 0x00020000
Normal Zone的PFN范围从0到0x20000,换算成物理地址既是从0到512M(0x20000*4K=512M,每个Page大小为4K)。那么0x20000是怎么得来的呢?只能去源代码中找答案了。arch_zone_highest_possible_pfn[ZONE_NORMAL] 值其实是从max_zone_pfns[ZONE_NORMAL]得来的,在paging_init函数中有max_zone_pfns[ZONE_NORMAL] = max_low_pfn; 所以0x20000其实就是全局变量max_low_pfn的值,它表示(猜测)低端内存最大允许的PFN值,对应的还有min_low_pfn. 这两个全局变量在bootmem_init()函数中计算。计算方法是: 遍历boot_mem_map,找到最大的RAM物理地址,计算其PFN赋给max_low_pfn。但是如果已经超过了HIGHMEM_START,则把HIGHMEM_START的PFN值赋给max_low_pfn。
在我们的系统中HIGHMEM_START为512M,而启动参数配置的最大RAM地址0x37600000(即768M+118M=886M)已经超过了512M,所以max_low_pfn=0x20000.
[ 0.000000] HighMem 0x00020000 -> 0x00037600
如果启动参数配置的最大RAM地址超过了HIGHMEM_START,那么超过的部分就属于ZONE_HIGHMEM了。highstart_pfn就是0x20000, 而highend_pfn则是配置的最大RAM,即0x37600000, 换算成pfn就是0x00037600.TODO: 如果板子上的RAM实际只有512M,那么配置了highmem会怎么处理呢?会映射到真正的RAM上吗?
[ 0.000000] Movable zone start PFN for each node
我们没有配置Movable Zone, 所以这里什么也不打印。 启动参数kernelcore和movablecore可以用来指定Movable Zone的大小。[ 0.000000] early_node_map[2] active PFN ranges [ 0.000000] 0: 0x00000000 -> 0x00009700 [ 0.000000] 0: 0x00030000 -> 0x00037600
这里才是真正使用到的PFN范围。也就是从0到151M, 768M到768+118=886M是从early_node_map数组中打印的,该数组由 add_active_range(0, start, end);填充。函数调用过程为:
head.S --> start_kernel --> setup_arch --> arch_mem_init --> bootmem_init --> add_active_range
/** * add_active_range - Register a range of PFNs backed by physical memory * @nid: The node ID the range resides on * @start_pfn: The start PFN of the available physical memory * @end_pfn: The end PFN of the available physical memory * * These ranges are stored in an early_node_map[] and later used by * free_area_init_nodes() to calculate zone sizes and holes. If the * range spans a memory hole, it is up to the architecture to ensure * the memory is not freed by the bootmem allocator. If possible * the range being registered will be merged with existing ranges. */