在启动过程期间,尽管内存管理尚未初始化,但内核仍然需要分配内存以创建各种数据结构。bootmem分配器用于在启动阶段早期分配内存。对于启动过程,应该本着简单的原则,因此内核开发者决定实现一个最先适配分配器(first-fit)用于在启动阶段管理内存。该分配器使用一个位图来管理页,位图bit位要和物理内存页数相同。bit=1表示用过的页,bit=0为空闲页。
在需要分配内存时,分配器逐位扫描位图,直到找到1个能提供足够连续页的位置,即所谓的最先最佳(first-best)或最先适配位置。
这个过程不是很高效,因为每次都必须从头扫描,在内核初始化之后不能将其用作内存管理,之后用的是伙伴系统。
setup_memory
|-> 确定可以的低端内存页桢
|->setup_bootmem_allocator
|->init_bootmem
|->register_bootmem_low_pages
|->reserve_bootmem(bootmap,bootmap_size)
|->调用reserve_bootmem分配特定的内存区
IA-32的初始化
setup_memory分析检测内存区,找到低端内存区中最大的编码,由于高端内存处理麻烦,所以在启动时候不考虑,我们可以通过内核的启动日志找到相关信息。
chechunli@chechunli-desktop:~ $ dmesg | more
......
[ 0.000000] 1086MB HIGHMEM available.
[ 0.000000] 896MB LOWMEM available.
......
然后,setup_bootmem_allocator接下来负责发起以后的所有步骤。首先要标记所有bit图中的页为已用,因为有些特殊页需要特殊处理,比如IA-32系统中的0页(该页在许多计算机上是一个特殊的BIOS页,有些特定于计算机的功能需要该页才能正常启动),内核映像占用的页。都标记为已用之后,再把那些潜在能用页释放出来,在IA-32上BIOS对该任务提供了支持,BIOS提供了可用的内存区列表,即初始化过程中更早一点提供的e820映射。
[ 0.000000] BIOS-provided physical RAM map:
[ 0.000000] BIOS-e820: 0000000000000000 - 000000000009f000 (usable)
[ 0.000000] BIOS-e820: 000000000009f000 - 00000000000a0000 (reserved)
[ 0.000000] BIOS-e820: 00000000000f0000 - 0000000000100000 (reserved)
[ 0.000000] BIOS-e820: 0000000000100000 - 000000007bed0000 (usable)
[ 0.000000] BIOS-e820: 000000007bed0000 - 000000007bed3000 (ACPI NVS)
[ 0.000000] BIOS-e820: 000000007bed3000 - 000000007bee0000 (ACPI data)
[ 0.000000] BIOS-e820: 000000007bee0000 - 000000007bf00000 (reserved)
[ 0.000000] BIOS-e820: 000000007c000000 - 0000000080000000 (reserved)
[ 0.000000] BIOS-e820: 00000000f0000000 - 00000000f4000000 (reserved)
[ 0.000000] BIOS-e820: 00000000fec00000 - 0000000100000000 (reserved)
停用bootmem分配器
在系统初始化进行到伙伴系统分配器能够承担内存管理的责任后,必须停用bootmem分配器,因为不能用两个分配器。首先,内核需要扫描bootmem分配器的页位图,释放未用的页。其次,页位图释放后,它占据的内存空间也需要释放。
释放初始化数据
许多内核代码块和数据表只在系统初始化阶段需要,所以需要释放掉。
内核提供了两个属性(__init, __initcall)用于标记初始化函数和数据。这些必须放在函数或者数据之前。
int __init hyper_hopper_probe(struct net_device *dev)
在数据段也可以标记为初始化数据,用__initdata。
__init和__initdata不能使用普通C编译,只适用于GNU C编译器。
#define __init __section(.init.text)__cold
#define __initdata __section(.init.data)
__cold通知编译器,通向该函数的代码路径可能性较低,该函数不会经常调用。
释放了多少内存可以通过启动日志查看
chechunli@chechunli-desktop:/ $ dmesg
......
[ 22.253823] Freeing unused kernel memory: 368k freed
......
阅读(1798) | 评论(0) | 转发(0) |