x86架构下的物理内存-通过linux概述

简介:

其它体系结构我没有深入研究过,然而对于x86而言,我们很多人都是很了解的。其内存可以支持4G(不考虑PAE),因为地址总线为32位,也就是说32条1位的线缆可以选择4G的地址,因此我们想当然的认为我们买了两条2G的内存插入以后,我们的系统就可以有4G的内存可用了,我们的系统内存在满载运行,然而果真如此吗?答案是否定的!
     因为所谓的地址总线32位是指从cpu引脚出来的总线是32位,是针对于cpu而言的,具体这些总线最终能全部连接在主板的ram上吗?会不会还会连接到其它的设备上呢?这要看主板怎么设计了。这里主板上的北桥芯片解除了cpu和设备之间的地址偶合,典型的设计为cpu出来的地址总线32位全部连接在北桥芯片之上,当cpu发出一个32位的地址比如0xcb000000的时候,由北桥来决定该地址发往何处,可能发往内存ram,也可能发往显示卡,也可能发往其它的二级总线,当然也可能发往南桥芯片(一个类似的解析地址的芯片,北桥解耦了cpu和主板芯片/总线,而南桥则解耦了主板芯片/总线和外部设备,比如ata硬盘,usb之类的设备就可以连接在南桥芯片上)。如果北桥选择将该地址发往PCI总线上,那么显然内存ram就收不到这个地址请求,而且自从主板设计好了之后,理论上该地址就永远被发送给了PCI,当然了,你可以通过诸如跳线之类的办法来更改之,(而且现在很多板子都有被bios“自动探测/识别/设置”的功能,此种情形下地址拓扑信息就不必记录在bios里面了,而是在bios开始运行的时候自动生成,生成的方式不外乎侦测-往特定针脚发送电平序列信号,然后得到回复,不过具体往哪里发送电平信息也必须由主板和cpu来确定标准),因此虽然你有4G的所谓的满载的ram,然而它的地址0xcb000000却不能被使用。以上仅仅是一个例子,主板上还有很多的设备或者总线会占据一些地址总线上的地址,这样说来你的4G的ram会有很多不能使用,典型的,intel提出了PAE,即物理地址扩展,使得可以支持4G以上的ram,实际上它的实现很简单,就是为ram增加几个地址总线位,变成36位的地址总线,这样就可以插入64G的ram了,这时4G以上的地址总线空间将不会被其它设备占据,而北桥只会将地址发往ram。
     既然4G的地址空间不能完全由ram内存条使用,那么ram不能使用哪些地址呢?这个信息很重要,因为这个信息会指导操作系统内核进行物理内存分配,比如其它地址使用的地址处的页面就不能被分配,否则就访问到设备了,因此这些个地址处的页面应该设置为保留,永远不能被使用,事实上,它们被浪费了。这些地址信息存放的位置是BIOS,BIOS里面存放着很重要的信息,这些信息可以组成一张逻辑拓扑图,真实反映主板上的芯片是如何排列放置的,待到主板上电后,主板上的芯片和总线就形成了一张真实的“地图”,在bios拓扑图的指导下被检测。
     既然BIOS里面存放拓扑图,那么操作系统内核在启动的时候怎样得到它呢,得到了它之后,操作系统才能建立自己的物理地址空间映射。得到bios信息的办法莫多于bios调用了,也就是0x15调用,参数由寄存器指定,如果你想得到地址信息,也就是那张拓扑图,那么你要将eax设置成0X0000E820,然后读取返回即可,以下是linux在拥有256M内存的机器上得到的地址信息,该信息在内核启动的时候通过bios调用得到:
BIOS-provided physical RAM map:
BIOS-e820: 0000000000000000 - 000000000009f800 (usable)
BIOS-e820: 000000000009f800 - 00000000000a0000 (reserved)
BIOS-e820: 00000000000dc000 - 00000000000e0000 (reserved)
BIOS-e820: 00000000000e4000 - 0000000000100000 (reserved)
BIOS-e820: 0000000000100000 - 000000000fef0000 (usable)
BIOS-e820: 000000000fef0000 - 000000000feff000 (ACPI data)
BIOS-e820: 000000000feff000 - 000000000ff00000 (ACPI NVS)
BIOS-e820: 000000000ff00000 - 0000000010000000 (usable)
BIOS-e820: 00000000fec00000 - 00000000fec10000 (reserved)
BIOS-e820: 00000000fee00000 - 00000000fee01000 (reserved)
BIOS-e820: 00000000fffe0000 - 0000000100000000 (reserved)
显然所有的ram都可以使用,毕竟它太小了。下面是一个拥有4G内存的机器的地址信息:
BIOS-provided physical RAM map:
BIOS-e820: 0000000000000000 - 000000000009f000 (usable)
BIOS-e820: 000000000009f000 - 00000000000a0000 (reserved)
BIOS-e820: 00000000000f0000 - 0000000000100000 (reserved)
BIOS-e820: 0000000000100000 - 00000000bdc90000 (usable)
BIOS-e820: 00000000bdc90000 - 00000000bdce3000 (ACPI NVS)
BIOS-e820: 00000000bdce3000 - 00000000bdcf0000 (ACPI data)
BIOS-e820: 00000000bdcf0000 - 00000000bdd00000 (reserved)
BIOS-e820: 00000000e0000000 - 00000000f0000000 (reserved)
BIOS-e820: 00000000fec00000 - 0000000100000000 (reserved)
BIOS-e820: 0000000100000000 - 0000000140000000 (usable)
可见0000000000100000 - 00000000bdc90000总共3G左右的ram是可以被OS使用的,它们是可以被寻址的,而其它的将近1G的ram被设置为reserved,它们被浪费了。在linux的/proc/meminfo中MemTotal:一行所显示的就是0x00000000bdc90000和0000000000100000的差,具体这些地址被用于什么设备,从/proc/iomem中可以看到,以下是256M内存机器的iomem:
00000000-0000ffff : reserved
00010000-0009f7ff : System RAM
0009f800-0009ffff : reserved
000a0000-000bffff : Video RAM area
000c0000-000c7fff : Video ROM
000c8000-000c8fff : Adapter ROM
000c9000-000c9fff : Adapter ROM
000ca000-000cafff : Adapter ROM
000dc000-000dffff : reserved
000e4000-000fffff : reserved
  000f0000-000fffff : System ROM
00100000-0feeffff : System RAM
  01000000-01242d96 : Kernel code
  01242d97-0133c847 : Kernel data
  01394000-013cc243 : Kernel bss
0fef0000-0fefefff : ACPI Tables
0feff000-0fefffff : ACPI Non-volatile Storage
0ff00000-0fffffff : System RAM
10000000-1000ffff : 0000:00:11.0
10010000-1001ffff : 0000:00:12.0
10020000-1002ffff : 0000:00:13.0
10030000-10037fff : 0000:00:0f.0
10038000-1003bfff : 0000:00:10.0
e8000000-e87fffff : 0000:00:0f.0
e8800000-e8800fff : 0000:00:10.0
f0000000-f7ffffff : 0000:00:0f.0
fec00000-fec0ffff : reserved
  fec00000-fec00fff : IOAPIC 0
fee00000-fee00fff : Local APIC
  fee00000-fee00fff : reserved
fffe0000-ffffffff : reserved   //这里一般是bios,凡是cpu发出的到这些地址的访问,全部被路由到bios
再次重申一遍,是4G以下的某些地址预留给了设备而不是4G以下的ram预留给了设备,很多设备是不使用ram芯片作存储的,它们只是占用了一些地址而已。比如上述iomem的内容中000c8000-000c8fff : Adapter ROM就是将前面的地址预留给了ROM芯片,cpu发出对该地址段中的一个的访问时,芯片组会将地址总线信号发往ROM而不是内存ram。
     在linux中,内核启动的时候,在很早的阶段,内核调用detect_memory来通过bios探测内存,它又调用detect_memory_e820来探测地址信息:
static int detect_memory_e820(void)
{
    int count = 0;
    struct biosregs ireg, oreg;
    struct e820entry *desc = boot_params.e820_map;
    static struct e820entry buf; /* static so it is zeroed */
    initregs(&ireg);
    ireg.ax  = 0xe820;
    ireg.cx  = sizeof buf;
    ireg.edx = SMAP;
    ireg.di  = (size_t)&buf;
    do {
        intcall(0x15, &ireg, &oreg); //调用bios中断
        ireg.ebx = oreg.ebx; 
        if (oreg.eflags & X86_EFLAGS_CF)
            break;
        if (oreg.eax != SMAP) {
            count = 0;
            break;
        }
        *desc++ = buf;
        count++;
    } while (ireg.ebx && count < ARRAY_SIZE(boot_params.e820_map));
    return boot_params.e820_entries = count;
}
这个函数调用完毕之后,boot_params.e820_map中就保存了“哪段地址是干什么用”的信息,将来linux在初始化物理内存的时候将使用这个信息,比如将保留给设备的地址处的页面设置为reversed,这样在分配物理页面的时候,会绕过这些被保留的地址处的页面。
附:察看linux中的内存的问题
在linux中可以通过top,free,/proc/meminfo等多种方式查看系统的内存,然而不同的内核编译选项编译出来的内核显示出来的内存总量却是不同的,在编译了HIGHMEM64G,也就是打开了PAE的情况下编译的内核,查到的内存总量会包括预留地址处的内存页面,而不打开PAE则不计算这些页面,显然打开PAE时的计算方式是不合理的,毕竟既然有那么多内存就应该可被使用,而实际上那些页面是不能使用的。
附:x86体系的地址映射图(注意,是地址,而不是ram)

mem


 本文转自 dog250 51CTO博客,原文链接:http://blog.51cto.com/dog250/1271101


相关文章
|
29天前
|
存储 前端开发 Java
Kotlin教程笔记 - MVVM架构怎样避免内存泄漏
Kotlin教程笔记 - MVVM架构怎样避免内存泄漏
26 2
|
2月前
|
存储 前端开发 Java
Android MVVM架构模式下如何避免内存泄漏
Android采用MVVM架构开发项目,如何避免内存泄漏风险?怎样避免内存泄漏?
109 1
|
23天前
|
机器学习/深度学习 弹性计算 人工智能
阿里云服务器架构有啥区别?X86计算、Arm、GPU异构、裸金属和高性能计算对比
阿里云ECS涵盖x86、ARM、GPU/FPGA/ASIC、弹性裸金属及高性能计算等多种架构。x86架构采用Intel/AMD处理器,适用于广泛企业级应用;ARM架构低功耗,适合容器与微服务;GPU/FPGA/ASIC专为AI、图形处理设计;弹性裸金属提供物理机性能;高性能计算则针对大规模并行计算优化。
|
1月前
|
Java Linux Android开发
深入探索Android系统架构:从Linux内核到应用层
本文将带领读者深入了解Android操作系统的复杂架构,从其基于Linux的内核到丰富多彩的应用层。我们将探讨Android的各个关键组件,包括硬件抽象层(HAL)、运行时环境、以及核心库等,揭示它们如何协同工作以支持广泛的设备和应用。通过本文,您将对Android系统的工作原理有一个全面的认识,理解其如何平衡开放性与安全性,以及如何在多样化的设备上提供一致的用户体验。
|
2月前
|
Docker 容器
docker:记录如何在x86架构上构造和使用arm架构的镜像
为了实现国产化适配,需将原x86平台上的Docker镜像转换为适用于ARM平台的镜像。本文介绍了如何配置Docker buildx环境,包括检查Docker版本、安装buildx插件、启用实验性功能及构建多平台镜像的具体步骤。通过这些操作,可以在x86平台上成功构建并运行ARM64镜像,实现跨平台的应用部署。
1562 2
|
2月前
|
存储 固态存储 安全
阿里云服务器X86计算架构解析与X86计算架构云服务器收费价格参考
阿里云服务器架构分为X86计算、Arm计算、高性能计算等多种架构,其中X86计算是用户选择最多的一种架构,本文将深入探讨阿里云X86计算架构的云服务器,包括其技术特性、适用场景、性能优势以及最新价格情况。
|
2月前
|
存储 缓存 数据处理
简述计算机X86架构
【10月更文挑战第3天】本文介绍了计算机的基本工作原理,重点阐述了CPU(中央处理器)及其组成部分:运算单元、数据单元和控制单元的功能。文中解释了CPU通过总线与内存等设备通信的过程,并详细描述了指令执行的步骤,包括指令获取、数据处理和结果存储。此外,还介绍了地址总线和数据总线的作用,以及段寄存器在内存管理中的应用。最后,提供了一些基本的CPU指令示例。文中配有多幅插图帮助理解。
|
2月前
|
存储 前端开发 Java
Kotlin教程笔记 - MVVM架构怎样避免内存泄漏
Kotlin教程笔记 - MVVM架构怎样避免内存泄漏
|
2月前
|
存储 前端开发 Java
Kotlin教程笔记 - MVVM架构怎样避免内存泄漏
Kotlin教程笔记 - MVVM架构怎样避免内存泄漏
59 0
|
16天前
|
弹性计算 API 持续交付
后端服务架构的微服务化转型
本文旨在探讨后端服务从单体架构向微服务架构转型的过程,分析微服务架构的优势和面临的挑战。文章首先介绍单体架构的局限性,然后详细阐述微服务架构的核心概念及其在现代软件开发中的应用。通过对比两种架构,指出微服务化转型的必要性和实施策略。最后,讨论了微服务架构实施过程中可能遇到的问题及解决方案。
下一篇
DataWorks