天天看点

进程的虚拟地址空间(分段/分页)

演变过程

早期的内存分派过程:运行起来一个进程就会从内存中分配一块连续的空间提供其使用,这样存在很多的问题

1)内存使用率低,当我拥有了足够大的空间不一定能够运行起来对应大小的程序,见下图

2)缺乏访问控制,当我的一个程序恶意的去该表另一个程序的数据时,并不会受到限制,这样会造成严重的后果

3)程序运行的地址不确定,因为所有地址都是物理地址,只有程序运行起来才能够知道

进程的虚拟地址空间(分段/分页)

分段

为了解决以上问题,采用了分段式内存管理,就是增加一个中间层,利用一种间接的地址访问方法访问物理内存,程序中访问的内存地址不再是实际的物理内存地址,而是一个虚拟地址,然后由操作系统将这个虚拟地址映射到适当的物理内存地址上,这样就可以保证内存访问控制,程序本身只能访问本身的虚拟地址,虚拟地址被操作系统映射到物理地址,只要操作系统确保映射到的物理地址的隔离性,就可以最终实现程序隔离的作用

进程的虚拟地址空间(分段/分页)

分页&&虚拟地址空间

但是分段虽然解决了内存访问控制的问题,但是它并不能解决内存使用率低的问题,于是提出分页式内存管理 ,分页式内存管理可以将一段程序加载到不连续的物理空间上,但是从虚拟地址空间来看依旧是连续的,这样就可以解决内存使用率低的问题

1)首先解释进程的虚拟地址空间

一个运行起来的进程操作系统都维护了一个task_struct结构体,存储了进程的信息,其中有一个mm_struct类型的结构体指针,这个指针指向的就是进程的虚拟地址空间

实现方式:mm_strcut结构体也叫内存描述符,其中记录了各个段的起始地址,终了地址,通过这种方式描述了进程的虚拟地址空间

进程的虚拟地址空间(分段/分页)

2)分页

不管是虚拟地址空间还是物理地址空间,操作系统都将其分为了一个个大小相同的内存页,假设页大小为4k,那么对于一个4G的内存,就有大约100多万个内存页,内存地址分为页号(前20位)页内偏移(后12位)

进程的虚拟地址空间(分段/分页)

页表: 页表中存储的是一个虚拟地址和一个物理地址的映射关系,映射方法是通过虚拟地址页号在页表中找到对应的物理页号,将虚拟地址页号偏移作为物理地址页号偏移,组成物理地址,页表除了映射虚拟地址和物理地址外,还标记了物理地址的访问权限

进程的虚拟地址空间(分段/分页)

分析上图:进程task_struct结构体中有个mm_strcut结构体,这个结构体就是虚拟地址空间,页表将虚拟地址空间映射到物理地址空间,物理地址空间不一定是连续的,但是虚拟地址空间可以做到连续,既保证了内存访问控制,也保证了内存的使用率

对于父进程和子进程,因为创建子进程时是拷贝父进程得到,并且利用写实拷贝技术,因此创建了子进程还没有改变数据的时候,父子进程共享同一份数据,例如一个变量g_val,那么父子进程变量地址相同,但是你会发现即使改变了一方的数据(数据不同一定存储在不同的地址上)得到的变量地址依旧相同,这是因为得到的并不是物理地址,而是虚拟地址,虚拟地址虽然相同但是通过不同的页表映射到不同的物理地址上

访问内存的过程: 当访问虚拟内存时,会通过虚拟地址页号访问 MMU(内存管理单元) 去匹配对应的物理地址页号,如果找不到虚拟内存和物理内存的映射关系,会产生缺页中断,从磁盘中取得缺的页放入内存,如果内存已满,还会根据某种算法将磁盘中的页换出(MMU中存储页表,用来匹配虚拟内存和物理内存),找到后将虚拟地址页内偏移作为物理地址页内偏移从而构成物理地址

段页式内存管理

进程的虚拟地址空间(分段/分页)

段页式访问内存的过程: 通过段号在段表中找到对应的段内页表信息,通过段内页号在页表中找到对应的物理页号,找到后将虚拟地址页内偏移作为物理地址页内偏移从而构成物理地址

多级页表

对于一个4G的内存,页表就有100多万个页表项,而这么多页表项我们并不是每个都能用到,每个进程我们都创建一个这样的页表也太浪费了,因此可以创建一个二级页表,这个二级页表专门管理一级页表,只有用到了我才给进程分配,类似于一级页表管理物理地址,只有用到了物理地址我才给进程开辟空间,否则我就不开辟

本博客两张彩色图片来源

分页内存管理&&虚拟地址空间的好处

1)内存访问控制

2)物理地址的利用率高

3)保证进程的独立性

写时拷贝技术

父进程创建了子进程,但是并没有直接给子进程开辟内存,拷贝数据,而是跟父进程映射到同一位置,但是如果内存中数据发生了改变,那么对于改变的这块内存,需要重新给子进程开辟内存,并且更新页表信息。提高子进程创建性能

继续阅读