网站a记录吗,宽带营销策略,网站优化过度被k,网站后台生成文章很慢一个程序#xff08;如王者荣耀#xff09;平常是存储在硬盘上的#xff0c;运行时才把这个程序载入内存#xff0c;CPU才能执行。
问题#xff1a; 这个程序载入内存的哪个位置呢#xff1f;载入内核所在的空间吗#xff1f;系统直接挂了。
一、虚拟内存
1.1 内存分…一个程序如王者荣耀平常是存储在硬盘上的运行时才把这个程序载入内存CPU才能执行。
问题 这个程序载入内存的哪个位置呢载入内核所在的空间吗系统直接挂了。
一、虚拟内存
1.1 内存分段管理
最早的程序员写代码时是需要指定程序在内存的运行位置的也即他们使用绝对地址来进行内存访问的。 计算机刚启动时采用实模式运行即使用绝对地址来进行内存访问操作系统加载完成会变成保护模式即使用虚拟地址进行内存访问。 问题来了
在有限的物理内存空间内多道程序是无法并行运行的举个栗子王者荣耀需要在内存地址0x0000 ~ 0x6000上微信需要在内存地址 0x0000 ~ 0x3000这下好了谁也跑不起来直接崩了程序员需要关注自己写的程序要跑在多大的物理内存上内存地址怎么分配……这耦合性也太高了而且不安全容易访问到其他程序已使用的物理内存。
解决方法很简单所有程序无需关心内存物理地址代码经过编译链接看到的地址都一样从 0 开始到最大地址的空间这个地址空间是独立的是该程序私有的其它程序既看不到也不能访问该地址空间这个地址空间和其它程序无关记为虚拟内存Virtual memory 虚拟内存地址记为虚拟地址物理内存的地址记为物理地址。 程序运行时找一块空闲物理内存装入即可。比如之前例子 王者荣耀(虚拟地址0x0000 ~ 0x6000)运行在物理内存0x0000 ~ 0x6000微信虚拟地址0x0000 ~ 0x3000运行在0x6000 ~ 0x9000 上述分配意味着我们需要记录虚拟空间和物理空间的映射关系我们将这个表记为段表因为映射的一段完整连续的物理内存。 进程虚拟地址物理地址王者荣耀0x0000 ~ 0x60000x0000 ~ 0x6000微信0x0000 ~ 0x30000x6000 ~ 0x9000 段表实际记录的是段的基址和边界使用时通过段表找到物理内存地址再结合段内偏移量即可定位数据。 上述为内存分段管理大致原理实际内存分段管理和早期硬件发展相关分为几个大的段同时配合段寄存器使用。 cs: 代码段 ds: 数据段 ss: 栈段 es扩展段 1.2 内存分页管理
前面我们成功的让2个程序同时正确的跑起来了但是物理内存是有限的如之前所示最大地址为0xA000现在想听网易云音乐虚拟地址为 0x0000 ~ 0x4000这就尴尬了空闲的物理内存0x9000 ~ 0xA000不够支持网易云音乐了但现实中我们上述需求的确可以同时满足啊 上述无法使用的内存空间0x9000 ~ 0xA000称为内存外部碎片即分段管理存在明显的外部碎片相对应的已分配过程序内存内部存在没有使用到的空间称为内存内部碎片。 其实也很简单我们把程序分批次装入物理内存中每次只载入一部分来满足运行即可我们把这部分程序内容记为 page 页存放页的物理内存区域记为page frame (页框)。 管理内存的最小单位是页;分页管理没有外部碎片了每一个页框均可使用。 实际当中每次载入多少数据合适呢一般为 4KB即 页的大小为4KB页框的大小也为 4KB。将物理内存和虚拟内存按照页的大小4KB 分割需要哪页就加载那页内容找一个空闲的页框存放即可。这样我们在有限的物理内存内载入并正确并行了多道程序。 按照上述策略我们成功得将3个程序并行跑起来了如下图所示 1.2.1 页表
虚拟内存页和物理内存页的映射关系也得占用物理内存进行存储即上图所示的 页表一个页表也占用一个页框存储即一个页表最大为 4KB。下图即为页表图示观察下图虚拟页号就是下标所以页表可以用一个一维数组存储即可数组下标为虚拟页号数组内容为物理页号和物理内存起始地址等其它额外信息 页面大小为4kByte因此每个页框的低12位均为0。内核将低12位充分利用每个位都表示对应虚拟页的相关属性。同样的骚操作在64位JVM中指针压缩也有。 1.2.2 物理地址计算
给定虚拟地址 0x0809 怎么计算物理地址呢
根据虚拟地址和页大小4KB按Byte编址需要 12 个 bit 计算虚拟页号和页内偏移量 虚拟页号 00x0809 / 4k 页内偏移量 90x0800 % 4k 页表基址寄存器找到页表物理地址查页表找到物理页号根据物理页号起始地址和页内偏移量得到物理地址。
计算机中完成上述地址转换的部件由特定硬件完成叫做 MMUMemory Management Unit内存管理单元它位于CPU 芯片中。
1.2.3 缺页中断
因为我们只将程序部分页面载入到内存当中当运行完这些页面继续往后运行时进程访问的虚拟地址在页表中查不到时即后续页面还在磁盘上此时 MMU 会触发缺页中断page fault从磁盘上将对应页面加载到物理内存中空闲页框内同时更新页表。
1.2.4 多级页表
在 32 位的环境下每个进程的虚拟内存为4GB一个页表最大为4KB页内偏移量12 bit剩余 20bit (32-12) 用于页号可以表示约 1,000,000220页, 页表中每一项记为页表项需要 4Byte所以4GB空间需要4MB220 * 4Byte物理内存来存储页表。
这意味着如果计算机并行10个进程的话需要 40MB 内存实际中开启的进程远不止10个需要更大内存来存储页表。
所以不可能将页表4MB全部放进内存又必须能看到页表全貌怎么办 兵仙韩信说只需十个将军即可统帅百万大军 同理只需一个页表索引页目录即可查找百万页表项即 一页可以放10244KB / 4Byte个页索引二级可以存储即可表示 2201024 * 1024页刚好覆盖整个 4GB 4KB * 220 虚拟地址空间。 使用时通过一级页表(记为页目录)找到二级页表从而得到最终物理地址若二级页表不存在触发缺页中断进行加载即可这不是一颗B树吗树高2。
1.2.4.1 多级页表物理地址
在 32 位的环境下我们将虚拟地址原页号进行分割10bit 存放一级页号页目录10bit 存放二级页号页表项如下图所示在 64 位的环境下二级分页是无法满足的实际使用时分为四级索引即B树高度为4 四级索引前面3级为页目录叶子节点为页表依次命名为 全局页目录项 PGDPage Global Directory上层页目录项 PUDPage Upper Directory中间页目录项 PMDPage Middle Directory页表项 PTEPage Table Entry 页表项为 8Byte一页4KB可以索引 512 4KB / 8Byte 个页四级可以索引 5124 个页可以表示 256TB (4KB * 5124) 虚拟空间512 个页号使用 9 (29 512)个 bit就可以表示四级页表使用 364 * 9个bit可以表示64 位环境用 4836 12个bit 进行寻址意味着PGD中 高位 16 bit 全0或者全1表示 256TB 空间。 小节虚拟地址本质上是索引偏移量。
1.2.5 页表缓存TLBTranslation Lookaside Buffer
计算机中为协调CPU和内存的访问速度一次内存的访问大约需要 120 个 CPU Cycle中间加了 高速缓存(CPU Cache) 。 高速缓存使用特定的由 SRAM 静态RAM random-access memory随机访问存储器 组成的物理芯片内存使用DRAM动态RAM高速缓存为3级L1/L2/L3 Cache。其中 L1/L2 是 CPU 私有L3 是所有 CPU 共享。缓存行(Cache Line)高速缓存的最小单元一次从内存中读取的数据大小。常用的 Intel 服务器 Cache Line 的大小通常是 64 字节。CPU Cache 的命中率通常能达到 95% 以上。 我们前面提到的 页表项PTE 同样需要从内存加载到高速缓存来提高CPU访问速度由于高速缓存空间远小于内存空间所以只能缓存程序中最常访问的页表项我们把缓存页表项的这块区域称为转址旁路缓存TLBTranslation Lookaside Buffer也称为快表内存中的表相应为慢表。 1.3 内存段页式管理
内存管理最开始分段管理后面又提出分页管理由于硬件和版本限制现在操作系统中的内存管理理论上是由段页结合一起管理的即先分段然后段内分页。 实际使用时操作系统做了简化将代码段和数据段的基址设为0段空间大小为这个虚拟内存的大小。 即现代操作系统中分段管理名存实亡。 在 CPU 中程序使用逻辑内存地址 1.4 虚拟内存空间整体布局
虚拟内存空间并非全部留给程序一般而言高地址段为内核空间用于系统内核使用低地址段为用户空间留给程序使用如下图所示 1.4.1 用户态虚拟内存
程序是由源代码经过编译、链接形成的可执行文件ELF。ELF中分为代码段.text、数据段.data、未初始化数据段.bss等很多逻辑段虚拟内存为保持这种程序的逻辑性同样也在逻辑上将内存划分为代码段、数据段、堆、文件映射与匿名映射区、栈等。 注意 一个段segment一般包含多个页(page)每个段的长度并不相等保留区这段虚拟内存是不可访问的因为在大多数操作系统中数值比较小的地址通常被认为不是一个合法的地址这块小地址是不允许访问的。比如在 C 语言中我们通常会将一些无效的指针设置为 NULL指向这块不允许访问的地址。栈和文件映射区使用时均是从高地址向低地址分配堆使用时从低地址向高地址分配不可访问段仅存在于64位环境中为了防止程序在读写数据段的时候越界访问到代码段这个保护段可以让越界访问行为直接崩溃防止它继续往下运行。通过 cat /proc/pid/maps 或者 pmap pid 查看进程的虚拟内存空间布局以及其中包含的所有内存区域。进程进入内核态之后使用的仍然是虚拟内存地址只不过在内核中使用的虚拟内存地址被限制在了内核态虚拟内存空间范围中。 虚拟内存的内容暂时介绍到这里后续会介绍物理内存的实际分配情况。