速通虚拟和物理内存
物理内存与虚拟内存
一 ,什么是物理内存?
物理内存(RAM)就是电脑里真实存在的内存空间,也就是内存条
电脑里有 CPU、RAM(内存)、SSD(硬盘)
- CPU:负责执行指令,进行计算
- RAM:存放程序运行时需要用到的数据
- SSD:用来存放文件和程序,关机后数据也能保存
比如电脑安装了 16GB 的内存条,就有大约 16GB 的物理内存
当我们运行一个 C 程序时,程序需要的数据会按需要放到 RAM 中,然后 CPU 再去访问这些数据
RAM 的读取速度比 SSD 快得多,但是断电后,RAM 中的数据一般就没了
二 ,什么是虚拟内存?
虚拟内存不是电脑里真的多出来了一块内存,而是操作系统给程序提供的虚拟地址空间
#include <stdio.h>
int main()
{
int a=10;
printf("%p\n",(void *)&a);
return 0;
}
分析
&a:取出变量 a 的地址%p:输出地址
这里输出的一般是虚拟地址,并不是数据在内存条里的实际地址
-
比如程序 A 和程序 B
程序 A:虚拟地址 1000 → 物理地址 5000
程序 B:虚拟地址 1000 → 物理地址 8000 -
两个程序的虚拟地址虽然相同,但是对应的物理地址可以不同
也就是说,程序使用的是虚拟地址,数据真正存放的位置是在物理内存里
为什么要有虚拟内存
假如程序直接使用物理地址,不同程序之间就容易互相影响
操作系统给每个程序分配自己的虚拟地址空间,这样的话
- 不同程序一般不能随便访问对方的内存
- 程序不用自己去找物理内存的位置
- 申请了虚拟内存,也不代表马上就用了同样大小的物理内存
虚拟内存不等于 Swap(交换空间),这两个不是一个东西
虚拟内存的划分
- 代码段 - - -存放程序的指令
- 数据段 - - -存放全局变量和静态变量
- 堆 - - - 程序自己申请的内存
- 栈 - - - 函数的临时工作台
- 内存映射区 - - -链接文件与内存的区域
三, 虚拟地址是怎么转换成物理地址的?
1. 页
操作系统不是一个字节一个字节地管理内存,而是把内存分成一块一块的,这一块就叫页
比如很多 Linux 电脑的一个页是 4096 字节,也就是 4KiB
不同电脑的页大小不一定相同
2. 页表
页表用来记录虚拟页和物理页之间的对应关系
比如
虚拟页 1 -> 物理页 8
虚拟页 2 -> 物理页 3
虚拟页 3 -> 物理页 12
程序访问虚拟地址时,需要根据页表找到它对应的物理地址
3. MMU 和 TLB
- MMU:CPU 里负责把虚拟地址转换成物理地址的硬件
- TLB:用来保存最近使用过的地址对应关系,避免每次都去查页表
如果 TLB 里有对应关系,就可以直接使用,没有的话,再查页表
TLB 没查到,不代表发生了缺页异常
4. 缺页异常
比如程序申请了一块内存,但是还没有真正使用,这时操作系统可能还没有给它分配对应的物理内存,等程序第一次访问这块内存时,就可能发生缺页异常
- 如果是正常的内存访问,操作系统可以分配物理内存,然后继续运行
- 如果数据暂时在硬盘上,就可能需要先把数据读回来
- 如果访问了不允许访问的地址,就可能出现段错误
缺页异常不一定是程序出错,也可能是操作系统正常分配内存的过程
四, Swap 是什么?
Swap 就是交换空间,一般使用硬盘上的一部分空间
-
比如电脑只有 16GB 的 RAM,但是同时运行的程序比较多,物理内存不太够用,Linux 就可能把暂时不用的一些内存页放到 Swap 里,等需要这些数据时,再把它们读回来
-
Swap 用的是硬盘空间,不是真的增加了内存条
-
SSD 比 RAM 慢很多,所以频繁使用 Swap 可能会让电脑变卡
-
也不是 RAM 占用高就一定会用到 Swap,还要看系统设置
五, malloc 申请的是虚拟内存还是物理内存?
我们写 C 语言时,经常会用到 malloc
int *p=malloc(100*sizeof(int));
相当于申请一块能存放 100 个 int 的内存空间
但是 malloc 成功后,并不代表操作系统已经马上分配了同样大小的物理内存
在常见的 Linux 环境下,系统可能先准备好虚拟地址空间,等程序真正访问时,再分配对应的物理页
#include <stdio.h>
#include <stdlib.h>
#define SIZE (64*1024*1024)
int main()
{
char *p=malloc(SIZE);
if(p==NULL)return 1;
printf("第一次暂停\n");
getchar();
for(size_t i=0;i<SIZE;i+=4096)
{
p[i]=1;
}
printf("第二次暂停\n");
getchar();
free(p);
return 0;
}
分析
SIZE:申请64*1024*1024字节,也就是64MiBmalloc(SIZE):申请内存- 第一个
getchar():让程序先停下来,方便查看内存占用 p[i]=1:每隔 4096 字节写入一个数据。如果页大小是 4KiB,相当于每页都写入一次- 第二个
getchar():再次暂停,查看内存占用有没有变化 free(p):释放之前申请的内存,释放以后就不能再通过 p 使用这块空间
六, 用 Ubuntu 终端查看内存
1. 查看内存使用情况
终端输入
free -h
free:查看系统内存的使用情况-h:让输出的数字更容易看,比如用 MiB、GiB 表示Mem:物理内存Swap:交换空间
这里的
free是 Linux 命令,不是 C 语言里的free(p)。
2. 编译与运行代码
先将代码保存为 show.c,然后在终端输入:
gcc show.c -o show
./show
gcc show.c -o show:编译代码,生成show可执行文件./show:运行程序
程序第一次会停在:
第一次暂停
先不要按回车,再打开一个终端,输入:
ps -C show -o pid,comm,vsz,rss
分析
ps:查看进程信息-C show:只查看进程名为show的程序-o:指定输出哪些信息pid:进程编号comm:进程名称vsz:虚拟内存大小rss:当前驻留在物理内存中的大小
这里
VSZ和RSS的单位通常都是 KiB,不是字节
比如 RSS 显示 1680,就是 1680KiB,它不一定为 0,因为程序本身运行也要占用一些物理内存,并不只有 malloc 申请的那部分
然后回到第一个终端,按一次回车,让程序执行写入操作,再运行一次上面的 ps 命令
分析
- 执行
malloc后,VSZ已经增加,RSS很少 - 往内存里写入数据后,
VSZ不变,RSS会明显增加
申请了内存空间,不代表马上占用同样大小的 RAM,等真正访问以后,物理内存占用才可能增加
RSS也可能包含共享的内存页,不能直接理解成这个程序独占的全部内存
七 , 内存单位
1KiB = 1024字节1MiB = 1024KiB1GiB = 1024MiB
注意 free -h 和 ps 显示的单位可能不一样
总结
- 物理内存就是电脑里真实存在的 RAM
- 虚拟内存是操作系统给程序提供的地址空间,程序一般使用虚拟地址
- 页表和 MMU 用来完成虚拟地址到物理地址的转换,TLB 可以加快查找
- Swap 是交换空间,不等于虚拟内存
malloc申请成功后,物理内存不一定马上增加同样多,可以用VSZ和RSS观察
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐
所有评论(0)