draveness / blog-comments

面向信仰编程
https://draveness.me
140 stars 6 forks source link

为什么 Linux 需要虚拟内存 · Why's THE Design? - 面向信仰编程 · /whys-the-design-os-virtual-memory #197

Closed draveness closed 2 years ago

draveness commented 4 years ago

https://draveness.me/whys-the-design-os-virtual-memory/

BaoXuebin commented 4 years ago

对我这个不太熟悉计算机底层原理的开发者来说,看的似懂非懂。不过感谢博主的这个系列,受益良多。

draveness commented 4 years ago

对我这个不太熟悉计算机底层原理的开发者来说,看的似懂非懂。不过感谢博主的这个系列,受益良多。

感谢支持,不清楚的话可以说一说

AmazingRise commented 4 years ago

感谢博主的分享,受益匪浅。 但对于为什么每层的页表结构只能够负责 8 位虚拟地址的寻址?还是有些不解。 这个是只要做到字节对齐(4的倍数)就可以,或是有什么其它的限制? 还有,为什么文中提到的最底层的页表是12位?

先谢谢博主了!

BaoXuebin commented 4 years ago

@draveness

对我这个不太熟悉计算机底层原理的开发者来说,看的似懂非懂。不过感谢博主的这个系列,受益良多。

感谢支持,不清楚的话可以说一说

嗯,已买书准备深入学习;半路出家,始终很难理解某些基本概念,需要系统的补课:-)

draveness commented 4 years ago

感谢博主的分享,受益匪浅。 但对于为什么每层的页表结构只能够负责 8 位虚拟地址的寻址?还是有些不解。 这个是只要做到字节对齐(4的倍数)就可以,或是有什么其它的限制? 还有,为什么文中提到的最底层的页表是12位?

先谢谢博主了!

这个问题先放一放,看看其他人有没有想法

draveness commented 4 years ago

@draveness

对我这个不太熟悉计算机底层原理的开发者来说,看的似懂非懂。不过感谢博主的这个系列,受益良多。

感谢支持,不清楚的话可以说一说

嗯,已买书准备深入学习;半路出家,始终很难理解某些基本概念,需要系统的补课:-)

可以看看 Computer Systems: A Programmer's Perspective 很不错的书

shangwen commented 4 years ago

想问您是通过什么工具画图的 很简洁 很清晰

draveness commented 4 years ago

@shangwen 想问您是通过什么工具画图的 很简洁 很清晰

你看看文章最底下

liuyhAttach commented 4 years ago

个人觉得以下这2者之间应该要明确区分开来,便于理解

  1. 物理内存映射成虚拟内存,达到抽象和隔离的作用
  2. 因为 物理内存空间不足以支撑系统内所有进程的虚拟内存需求,利用页面置换来复用物理内存,部分进程的存储数据(暂时不需要使用的)被放入到磁盘空间中,这部分磁盘空间叫虚拟内存
draveness commented 4 years ago

个人觉得以下这2者之间应该要明确区分开来,便于理解

  1. 物理内存映射成虚拟内存,达到抽象和隔离的作用
  2. 因为 物理内存空间不足以支撑系统内所有进程的虚拟内存需求,利用页面置换来复用物理内存,部分进程的存储数据(暂时不需要使用的)被放入到磁盘空间中,这部分磁盘空间叫虚拟内存

你说的两者是指物理内存和虚拟内存么?

AmazingRise commented 4 years ago

这两者都是虚拟内存,但专业点来讲,一般更倾向于把后者称为交换

draveness commented 4 years ago

这两者都是虚拟内存,但专业点来讲,一般更倾向于把后者称为交换

文中没有提到 Swap 说的都是 Paging 页面调度

AmazingRise commented 4 years ago

这两者都是虚拟内存,但专业点来讲,一般更倾向于把后者称为交换

文中没有提到 Swap 说的都是 Paging 页面调度

噢 我回楼上那位

zu1k commented 4 years ago

@AmazingRise 感谢博主的分享,受益匪浅。 但对于为什么每层的页表结构只能够负责 8 位虚拟地址的寻址?还是有些不解。 这个是只要做到字节对齐(4的倍数)就可以,或是有什么其它的限制? 还有,为什么文中提到的最底层的页表是12位?

先谢谢博主了!

最后12位指的是一个内存页,12位正好是4K的页大小,表示数据在页内偏移量

至于为什么页表是8位地址寻址,这个不太清楚。前面的页表寻址是为了找后面的页表,按照4层页表结构来讲也已经能达到44位虚拟地址的寻址了

Coolxc commented 4 years ago

不知道为什么必须要8位诶,可能是为了完美的利用页面空间吧。对于64位的虚拟内存,如果低12位是偏移量,那么剩下52位作为页号。如果页面大小为4KB,页表项大小为4B,那么每个页面最多可以存放2的10次方个页表项也就是每级页表最多为10位。那么52位最少需要6级页表。这个假设不知道对不对欸。

zu1k commented 4 years ago

@YangQQ1312045515 不知道为什么必须要8位诶,可能是为了完美的利用页面空间吧。对于64位的虚拟内存,如果低12位是偏移量,那么剩下52位作为页号。如果页面大小为4KB,页表项大小为4B,那么每个页面最多可以存放2的10次方个页表项也就是每级页表最多为10位。那么52位最少需要6级页表。这个假设不知道对不对欸。

我刚刚仔细看了那个四级页表的文档,发现我们被作者的图误导了,人家文档上四级页表分别占了 8\9\9\9位,作者的图画的全是8位

draveness commented 4 years ago

@YangQQ1312045515 不知道为什么必须要8位诶,可能是为了完美的利用页面空间吧。对于64位的虚拟内存,如果低12位是偏移量,那么剩下52位作为页号。如果页面大小为4KB,页表项大小为4B,那么每个页面最多可以存放2的10次方个页表项也就是每级页表最多为10位。那么52位最少需要6级页表。这个假设不知道对不对欸。

我刚刚仔细看了那个四级页表的文档,发现我们被作者的图误导了,人家文档上四级页表分别占了 8\9\9\9位,作者的图画的全是8位

可以的,确实画错了,不过 4 级页表结构中每一级都是 9 位

image

UPDATES: 已经修复了相关的内容

knight0zh commented 4 years ago

大佬想请教下,go中迭代数组是快速的因为内存地址是连续的 这里的连续是虚拟内存地址吧,物理内存不一定也连续,这样也快吗?go自己的内存管理器申请的内存是连续的所以go得数组的地址在物理内存也是连续的,能不能这么理解

bloatfan commented 4 years ago

在 centos7.6 中使用 top 命令,看一个 java 程序、显示两列 (VIRT )和 (RES)

VIRT RES 9.6g 1.8g

想请教一下,VIRT 多出来的部分是存放到哪里的

bloatfan commented 4 years ago

@lslz627 在 centos7.6 中使用 top 命令,看一个 java 程序、显示两列 (VIRT )和 (RES)

VIRT RES 9.6g 1.8g

想请教一下,VIRT 多出来的部分是存放到哪里的

针对我这个问题,我写了一个 c 语言程序,验证我的想法,虚拟内存是程序想申请的,但是可能没使用,操作系统实际没分配,这里的 VIRT 只是一个虚拟的概念,不一定存在存放到哪里的问题

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(void)
{
    char* mem = (char*)malloc(1 * 1024 * 1024 * 1024);
    mem[1024 * 1024] = '1'; // 对于申请的 1G 空间,程序实际只使用了在 1M 偏移量的空间的 4KB 内存

    sleep(1000);

    free(mem);

    return 0;
}

gcc main.c

以下是 top 命令执行的结果

// 可以看到虚拟内存和我申请的差不多,1G
PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
22405 root      20   0 1052788    348    272 S   0.0  0.0   0:00.00 a.out

以下是通过 cat /proc/pid/smaps 查看内存使用情况

7fb58f16f000-7fb5cf170000 rw-p 00000000 00:00 0
Size:            1048580 kB      (c 语言程序申请的内存,虚拟内存,程序自身能看到这么多)
Rss:                   8 kB      (操作系统为 c 程序实际分配的内存)
Pss:                   8 kB
Shared_Clean:          0 kB
Shared_Dirty:          0 kB
Private_Clean:         0 kB
Private_Dirty:         8 kB
Referenced:            8 kB
Anonymous:             8 kB
AnonHugePages:         0 kB
Swap:                  0 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB
Locked:                0 kB
VmFlags: rd wr mr mw me ac sd
draveness commented 4 years ago

@knight0zh 大佬想请教下,go中迭代数组是快速的因为内存地址是连续的 这里的连续是虚拟内存地址吧,物理内存不一定也连续,这样也快吗?

切片访问元素快是因为内存连续,所以寻址比较快吧,物理内存连不连续应该不影响这个结果,访问虚拟内存的时候都需要做一次转换。

go自己的内存管理器申请的内存是连续的所以go得数组的地址在物理内存也是连续的,能不能这么理解

这个不一定

ehds commented 4 years ago

一个页占4KB即12位,剩余57-12=45=5*9,五级页表。

SimleLong commented 4 years ago

博主有计划写篇关于编译-链接相关的文章吗

Reso1mi commented 4 years ago

感谢大佬的分享。

(文章开头虚拟内存的作用 "虚拟内存可以利用磁盘起到缓存的作用,提高进程访问磁盘的速度" 似乎笔误了?应该是利用主存吧)


2020-09-14 UPDATES: 已修复

draveness commented 4 years ago

博主有计划写篇关于编译-链接相关的文章吗

暂时没有,Go 语言设计与实现里面有 Go 语言的编译过程 https://draveness.me/golang/

draveness commented 4 years ago

感谢大佬的分享。

(文章开头虚拟内存的作用 "虚拟内存可以利用磁盘起到缓存的作用,提高进程访问磁盘的速度" 似乎笔误了?应该是利用主存吧)

确实有问题,已修复

zinwalin commented 3 years ago

写得非常好,可读性很强。

beihai0xff commented 3 years ago

@ehds 一个页占4KB即12位,剩余57-12=45=5*9,五级页表。

逻辑顺序应该是反过来的,应该是因为一个页有 4KB 占用 12 位,一个页表占 9 位,所以四级页表共占用 12+4*9=48 位,五级页表增加了一级 48+9=57 位。

我查了一些资料也没找到为什么每一级页表要占用 9 位,这个值可能和 CPU 硬件有关(个人猜测)

MrGuin commented 3 years ago

感觉地址空间那块可以顺带提一下文件的加载,比如exec加载可执行文件时只创建页表映射,在执行过程中用到某一页触发缺页才将页从硬盘加载到内存中,按需加载而不是一开始全部从硬盘拷贝到内存里,更有助于理解虚拟地址空间的效果。

laotoutou commented 3 years ago

那虚拟内存是如何隔离各个进程空间的呢

draveness commented 3 years ago

@laotoutou 那虚拟内存是如何隔离各个进程空间的呢

各个进程会有独立的 Page Table 映射物理内存

lujin123 commented 3 years ago

因为页表的大小是4KB的话,64位的虚拟地址占用8B的大小,所以一个页表中可以有4KB/8B=2^9项,这样就最少需要9位才能表示

neOG commented 3 years ago

感谢分享。

natsuboy commented 3 years ago

文中“虚拟内存中的虚拟页(Virtual Page,PP)”,这里应该是VP?


2021-04-19 UPDATES:已修复

fighterhit commented 3 years ago

@draveness

@laotoutou 那虚拟内存是如何隔离各个进程空间的呢

各个进程会有独立的 Page Table 映射物理内存

那不同进程的 Page Table 隔离时又是怎么知道哪些内存能用哪些不能呢?是会扫描一下已分配的吗?

zzm996-zzm commented 3 years ago

为什么每层的页表结构只能够负责 9 位虚拟地址的寻址? 答: 在csapp中4级页表中一级页表负责512G,二级页表是1G 3级是2MB 4级是4kb(真实映射) 2^9 1G = 512G 2^9 2m = 1G 2^9 * 4kb = 2MB

最后映射一个页表4KB就是负责直接去查找PPN了

zzm996-zzm commented 3 years ago

64 位的虚拟内存在操作系统中需要多少层的页表结构才能寻址? 答: 应该就是4层把 48位虚拟地址 不过我不是很清楚这个虚拟地址的生成 我用go打印的是44位 c打印的是36位

zzm996-zzm commented 3 years ago

大佬 我还有一个疑问 我在csapp中好像没得到答案 就是虚拟地址一直说是隔离进程中地址冲突,就是映射的时候是如何判断不会冲突的 我知道每个进程有独立的页表,cpu生成的虚拟地址会访问页表再去组装成物理地址,但是在映射的时候如何保证不会冲突

taizilongxu commented 2 years ago

@zzm996-zzm 大佬 我还有一个疑问 我在csapp中好像没得到答案 就是虚拟地址一直说是隔离进程中地址冲突,就是映射的时候是如何判断不会冲突的 我知道每个进程有独立的页表,cpu生成的虚拟地址会访问页表再去组装成物理地址,但是在映射的时候如何保证不会冲突

我理解是操作系统负责管理内存, 程序里只知道0, 1, 2 偏移地址, 操作系统知道该程序映射的地址, 比如说地址加个前缀 000001111 + 0, 1, 2

好几年前学的了, 如有不对望大神指正

sea-ljf commented 2 years ago

博主好~请教个问题,文章中的 Page Replacement 与隔壁文章的 Swapping,关系是什么呢?

Page Replacement 是调度策略,Swapping 是行为?

TXYH1 commented 2 years ago

不知道是不是我没完全理解,我怎么感觉只说了 虚拟内存带来了什么,而不是在说为什么需要虚拟内存。 虚拟内存有他的好处,不用虚拟内存也不代表只有缺点。感觉没说到什么必要性

draveness commented 2 years ago

@TXYH1 不知道是不是我没完全理解,我怎么感觉只说了 虚拟内存带来了什么,而不是在说为什么需要虚拟内存。 虚拟内存有他的好处,不用虚拟内存也不代表只有缺点。感觉没说到什么必要性

你说了个啥...