Linux系统硬体模块之内存
一、概述
在Linux使用中,会发现Linux的空闲内存总是非常少,让人感觉内存总是不够用,其实这正是Linux内存管理的特性。
能使用的内存大小和CPU位数有关,在32位的CPU上,能用的内存大小是4G,在64位CPU上,是2^64。(这里不区分虚拟和物理)
二、分配流程
先了解内存分配的大概流程:
- 用户进程使用malloc介面分配虚拟内存
- 用户进程访问内存虚拟地址
- MMU单元将虚拟内存转换为物理内存
- MMU去内存读取页表
- MMU发现页表不存在,自动触发缺页异常(page fault)
- 执行内核中的page fault处理程序,分配物理内存,修改页表
- 异常处理完毕,返回步骤2执行相应的操作
两个名词概念
物理内存:对应物理上内存条上的内存(真实存在)
虚拟内存:从进程的角度看,是一个逻辑的概念(抽象存在)
三、分配细节
通过虚拟内存技术,让每个应用程序都认为自己拥有独立且连续的可用的内存空间(一段连续完整的地址空间),而实际上,它通常是被映射到多个物理内存段,还有部分暂时存储在外部磁碟存储器上,在需要时再载入到内存中来。
1、用户进程使用什么来进行虚拟内存的分配?
标准C库中,提供了malloc/free函数分配释放内存,这两个函数底层是由brk,mmap,munmap这些系统调用实现的
- brk是将数据段(.data)的最高地址指针_edata往高地址推;
- mmap是在进程的虚拟地址空间中(堆和栈中间,称为文件映射区域的地方)找一块空闲的虚拟内存
Malloc(<128K)时使用brk进行分配,malloc(>128K)时使用mmap进行分配
2、虚拟内存和物理内存的对应关系由谁来负责维护,如何对应?
- 页表的创建和维护都是由操作系统内核负责的
- 由页表来建立虚拟内存到物理内存之间的映射关系,页表就是在内存中的一张表,简单看做一张hash表,记录的是虚拟地址和物理地址的对应关系,每个虚拟地址对应一个表项,通过这张表,就能将虚拟地址转换为物理地址,也就能建立虚拟内存到物理内存的映射关系了
3、MMU去读取页表,那它是如何知道页表在哪的?
- CPU上有特别的寄存器(CR3),向其中写入页表的地址,MMU就知道了
4、每个进程都有自己的页表,那MMU怎么知道读取哪张?
- 操作系统在初始化或分配、释放内存时会执行一些指令在物理内存中填写页表,然后用指令设置MMU,告诉MMU页表在物理内存中的什么位置。
5、为啥页表会不存在呢?页表是何时创建的?
- 在完成虚拟地址的分配后,这块内存一般是没有物理页与之对应的(即没有页表),等到进程第一次读写A这块内存的时候,发生缺页中断(page fault)
6、page fault是如何创建页表的?
- page fault是硬体上的一种机制,当硬体检测到缺页异常时,主动触发,然后会自动跳转到异常处理程序处理。该异常处理程序是内核中提前注册好的,其中要做的主要操作,就是:分配物理内存,然后修改相应进程的页表,建立该物理内存和虚拟内存的映射关系(页表项)。异常跟中断类似,区别在于,中断是非同步的,由外设触发;异常是同步的,由CPU自己触发
四、开机时内存分配
开机时禁止了内存分页机制,这和原始的MS-DOS一样,只有1MB的可用存储地址空间,用户可以运行任何指令、也可以修改存储区域中的任何位置——在实模式中没有保护和特权指令的概念。
虽然在刚刚开机后只能使用1MB的存储地址空间,但是32位的Intel处理器第一条执行指令地址为0xFFFFFFF0(4GB存储空间的最后16位元组)