對於一個這樣的參數 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.
- */