拷贝可用内存区信息
来源:互联网 发布:mac摔了下屏幕不亮 编辑:程序博客网 时间:2024/05/23 01:12
5.2.1 拷贝可用内存区信息
首先setup_arch第一步要做的就是保存arch/x86/kernel/head_32.S初始化的new_cpu_data数据到boot_cpu_data中。new_cpu_data主要是保存CPU的相关信息,在哪儿初始化的?还记得我们在arch/x86/kernel/head_32.S中忽略过的checkCPUtype吗?就在那里,感兴趣的同学可以去探究一下。
784行,setup_memory_map()函数,进入start_kernel内核初始化函数中第一个内存管理函数就是由它来实现。
1204 void __init setup_memory_map(void)
1205 {
1206 char *who;
1207
1208 who = x86_init.resources.memory_setup();
1209 memcpy(&e820_saved, &e820, sizeof(struct e820map));
1210 printk(KERN_INFO "BIOS-provided physical RAM map:/n");
1211 e820_print_map(who);
1212 }
setup_memory_map()最终调用x86_init.resources.memory_setup()实现对e820内存图的优化,并根据boot_params中 e820_map字段的值来设置全局变量e820的值。这个全局变量是一个e820map结构:
struct e820map {
__u32 nr_map;
struct e820entry map[E820_X_MAX];
};
还记得吗?我们在执行实模式下代码main函数的时候调用了一个detect_memory_e820()函数,当时该函数通过BIOS服务程序int 0x15获得系统启动后的所有可用空间,共有boot_params.e820_entries个可用空间,每块空间作为boot_params.e820_map[]数组的元素,存放着他们的起始地址、大小和元素。
这里说个题外话,探测一个PC机内存的最好方法是就是通过调用INT 0x15,eax = 0xe820来实现。这个功能在2002年以后被所有PC机所使用,这是唯一能够探测超过4G大小内存的方案,当然,这个方法也可以被认为是内存的最终检测方法。实际上,这个函数返回一个非排序列表,这个列表包含了那些没有使用的项,并且可能返回存在覆盖的区域。在linux中每个列表项被存放在ES:EDI指定的内存区域中。每个项均有一定的格式:即2个8字节字段,一个2字节字段。我们前面看见了,对于内存探测的实现由函数detect_memory_e820来实现的,在这个函数中,使用了一个do...while()循环来实现,并将所探测的内容写入boot_params.e820_map数组中。
那么,x86_init.resources.memory_setup()是个什么函数呢?在arch/x86/kernel/x86_init.c的32行,我们看到又有一个__initdata的定义:
32struct x86_init_ops x86_init __initdata = {
33
34 .resources = {
35 .probe_roms = x86_init_noop,
36 .reserve_resources = reserve_standard_io_resources,
37 .memory_setup = default_machine_specific_memory_setup,
38 },
39
40 .mpparse = {
41 .mpc_record = x86_init_uint_noop,
42 .setup_ioapic_ids = x86_init_noop,
43 .mpc_apic_id = default_mpc_apic_id,
44 .smp_read_mpc_oem = default_smp_read_mpc_oem,
45 .mpc_oem_bus_info = default_mpc_oem_bus_info,
46 .find_smp_config = default_find_smp_config,
47 .get_smp_config = default_get_smp_config,
48 },
49
50 .irqs = {
51 .pre_vector_init = init_ISA_irqs,
52 .intr_init = native_init_IRQ,
53 .trap_init = x86_init_noop,
54 },
55
56 .oem = {
57 .arch_setup = x86_init_noop,
58 .banner = default_banner,
59 },
60
61 .paging = {
62 .pagetable_setup_start = native_pagetable_setup_start,
63 .pagetable_setup_done = native_pagetable_setup_done,
64 },
65
66 .timers = {
67 .setup_percpu_clockev = setup_boot_APIC_clock,
68 .tsc_pre_init = x86_init_noop,
69 .timer_init = hpet_time_init,
70 },
71
72 .iommu = {
73 .iommu_init = iommu_init_noop,
74 },
75
76 .pci = {
77 .init = x86_default_pci_init,
78 .init_irq = x86_default_pci_init_irq,
79 .fixup_irqs = x86_default_pci_fixup_irqs,
80 },
81};
这里,跟其他__initdata数据一样,在编译的时候就存放在init数据区了。那么x86_init.resources.memory_setup也就是37行的那个default_machine_specific_memory_setup函数了。所以我们看到这个函数:
1166char *__init default_machine_specific_memory_setup(void)
1167{
1168 char *who = "BIOS-e820";
1169 u32 new_nr;
1170 /*
1171 * Try to copy the BIOS-supplied E820-map.
1172 *
1173 * Otherwise fake a memory map; one section from 0k->640k,
1174 * the next section from 1mb->appropriate_mem_k
1175 */
1176 new_nr = boot_params.e820_entries;
1177 sanitize_e820_map(boot_params.e820_map,
1178 ARRAY_SIZE(boot_params.e820_map),
1179 &new_nr);
1180 boot_params.e820_entries = new_nr;
1181 if (append_e820_map(boot_params.e820_map, boot_params.e820_entries)
1182 < 0) {
1183 u64 mem_size;
1184
1185 /* compare results from other methods and take the greater */
1186 if (boot_params.alt_mem_k
1187 < boot_params.screen_info.ext_mem_k) {
1188 mem_size = boot_params.screen_info.ext_mem_k;
1189 who = "BIOS-88";
1190 } else {
1191 mem_size = boot_params.alt_mem_k;
1192 who = "BIOS-e801";
1193 }
1194
1195 e820.nr_map = 0;
1196 e820_add_region(0, LOWMEMSIZE(), E820_RAM);
1197 e820_add_region(HIGH_MEMORY, mem_size << 10, E820_RAM);
1198 }
1199
1200 /* In case someone cares... */
1201 return who;
1202}
首先1177行为所有可用内存区空间进行“消毒”。至于sanitize“消毒”算法,有兴趣的同学,特别是对信息安全感兴趣的同学可以去研究一下。最重点的是1181行调用append_e820_map函数,将boot_params.e820_map[]数组中所有的内存区物理信息拷贝到全局数据e820中,最终返回"BIOS-e820"字符串,赋给setup_memory_map()函数中的内部变量who。
之后setup_memory_map函数将e820的数据保存到e820_saved中,相当于备份。最后调用e820_print_map打印出内存分布图,注意这里,e820.nr_map就是可以的内存区的数量:
151void __init e820_print_map(char *who)
152{
153 int i;
154
155 for (i = 0; i < e820.nr_map; i++) {
156 printk(KERN_INFO " %s: %016Lx - %016Lx ", who,
157 (unsigned long long) e820.map[i].addr,
158 (unsigned long long)
159 (e820.map[i].addr + e820.map[i].size));
160 e820_print_type(e820.map[i].type);
161 printk(KERN_CONT "/n");
162 }
163}
793~796行,调用系统编译生成的数据来初始化init_mm的start_code、end_code、end_data以及.brk等字段;同时也初始化code_resource的start、end字段和data_resource的start、end字段以及bss_resource的start、end字段。这个init_mm还记得吗?初始化0号进程的时候,它的task_struct的active_mm就被赋成了这个。它是一个mm_struct结构,在编译vmlinux的时候就只给他初始化了一下字段:
struct mm_struct init_mm = {
.mm_rb = RB_ROOT,
.pgd = swapper_pg_dir,
.mm_users = ATOMIC_INIT(2),
.mm_count = ATOMIC_INIT(1),
.mmap_sem = __RWSEM_INITIALIZER(init_mm.mmap_sem),
.page_table_lock = __SPIN_LOCK_UNLOCKED(init_mm.page_table_lock),
.mmlist = LIST_HEAD_INIT(init_mm.mmlist),
.cpu_vm_mask = CPU_MASK_ALL,
};
这里初始化它的start_code、end_code、end_data以及.brk等字段分别为_text、_etext、_edata和_brk_end,是一步极其重要的过程,表明了当前0号进程进程的虚拟内存技术就可以用了。至于后面那几个resource,通过870~872行代码将其作为一个IO resource保存起来,有关IO端口的相关知识,请查阅博客“Linux I/O体系结构”。
- 拷贝可用内存区信息
- ios 设备可用内存信息
- android获取系统剩余可用内存信息
- 获取系统剩余可用内存信息
- 获取当前系统的可用内存信息
- Android 获取手机总内存和可用内存等信息
- Android 获取手机总内存和可用内存等信息
- Android 获取手机总内存和可用内存等信息
- Android 获取手机总内存和可用内存等信息
- Android 获取手机总内存和可用内存等信息
- Android 获取手机总内存和可用内存等信息
- Android 获取手机总内存和可用内存等信息
- Android 获取手机总内存和可用内存等信息
- Android 获取手机总内存和可用内存等信息
- Android 获取手机总内存和可用内存等信息
- Android 获取手机总内存和可用内存等信息
- Android 获取手机总内存和可用内存等信息
- Android 获取手机总内存和可用内存等信息
- 入道8年聊聊编程
- java代码优化编程(1)
- java代码优化编程(2)
- asp.net 仿百度动态加载下拉菜单
- Java语言中容易被人忽视的细节
- 拷贝可用内存区信息
- 获得总页面数
- HTML rel 属性详解
- binary search
- 着手建立内核永久页表
- Mime 类型列表
- 第二次启动分页管理
- Linux系统限制
- 乱七八糟