linux 64位 寻址空间_Linux内存管理机制

f12340c394c242ca36f98ab072f4810a.png

内存寻址方式的发展历程

  • 直接寻址, 程序都是通过硬编码的形式绝对定位到内存地址。这种情况下的程序都有明显的缺点:可控性弱、难以重定位、难以维护等
  • 分段, 段的地址存放在寄存器中, 把 1M 的空间分成数个 64K(16位寄存器可寻址)的段来管理, 8086 处理器为程序使用的代码段、数据段、堆栈段分别提供了专门的 16 位寄存器用于保存这些段的段基址。引入了分段机制后,程序的地址不再需要硬编码了,调试错误也更加容易定位,同时更加重要的是支持了更大的内存地址空间。

分页和虚拟内存

  • 虚拟内存, 允许程序使用的内存大于计算机的实际内存; 虚拟内存技术本质上将部分硬盘空间映射到内存中的一种技术,这样使得程序可以使用的内存空间变大了, 如果程序访问的地址不在内存中时(放在外部磁盘空间中),就需要将访问地址所对应磁盘空间的程序内容加载到内存中,在加载的过程中可能面临着旧程序内容的置换
  • 内存寻址过程分析, 逻辑地址到物理地址的转换过程, 逻辑地址 -->(分段机制) 线性地址 -->(分页机制) 物理地址
  • 逻辑地址:程序代码中一个操作数或者一条指令的地址
  • 线性地址:虚拟地址可映射 4G 内存空间(分页机制的产物,逻辑上连续)
  • 物理地址:处理器的引脚发送到内存总线上的电信号相对应
  • 分段机制
  • 代码中使用到的逻辑地址由两部分组成: 1). 段选择符: 16 位长的字段; 2). 段内偏移地址:32 位长的字段(最大的段大小为 4GB)
  • 段描述符, 段描述符存放在全局描述符表(GDT)和局部描述符表(LDT)中
  • 快速访问段描述符, 由段选择符来索引实际的段描述符还需要查找 GDT 或者 LDT 表, 为了加速逻辑地址到线性地址的转换过程,80x86提供了一种附加的非编程寄存器,即每当一个段选择符装入寄存器时,相对应的段描述符就由内存装入到这个非编程寄存器中
  • 地址转换的过程, 一个逻辑地址由段选择符和段内偏移组成,在转换成线性地址时, 分段单元执行以下操作:
    • 先检查段选择符的 TI 字段,判断这个段的段描述符是存放在 GDT 中还是 LDT 中
    • 然后由段选择符中的 Index 索引到实际的段描述符
    • 将 Index 索引到实际的段描述符的地址 * 8,再加上 gdtr 或者 ldtr 寄存器中的值。这个过程就完成了段起始的位置的计算
    • 最后把计算的结果加上逻辑地址中的段内偏移就得到了线性地址
  • 分页机制
  • 页, 对应着线性地址,线性地址被分成以固定大小为单位的组,称为页,页大小一般为4K
  • 页框, 对应着物理地址,物理地址也就是 RAM,被分成固定大小的页框,每个页框对应一个实际的页
  • 页表, 用来将页映射到具体的页框中的数据结构
  • 分页
    • 每个页的大小为 4KB,为了映射 4GB 的物理空间,页表中将会有 1MB(2^20) 的映射项, 避免每个程序保存大页表, 引出了多级页表的概念,也称为页目录
    • 线性地址的转换过程需要两步, 1). 查找页目录找到具体的页表; 2). 然后查找页表,找到具体的页
    • 线性地址, 分为3部分; 1). Directory:决定页目录中的目录项,10位大小; 2). Table:决定页表中的表项,10位大小; 3). Offset:页框的相对位置

用户空间

用户空间中进程的内存,称为进程地址空间 - 进程地址空间 - 每个进程都有一个32位或64位的地址空间,取决于体系结构, 一个进程可寻址4GB的虚拟内存(32位地址空间中),但不是所有虚拟地址都有权访问。对于进程可访问的地址空间称为内存区域。每个内存区域都具有对相关进程的可读、可写、可执行属性等相关权限设置。 - 内存区域包含 - 代码段(text section): 可执行文件代码 - 数据段(data section): 可执行文件的已初始化全局变量(静态分配的变量和全局变量) - bss段: 程序中未初始化的全局变量,零页映射 - 进程用户空间栈的零页映射 - 每一个诸如C库或动态连接程序等共享库的代码段、数据段和bss也会被载入进程的地址空间 - 任何内存映射文件 - 任何共享内存段 - 任何匿名的内存映射 - 内存描述符

struct mm_struct
{
  struct vm_area_struct *mmap;
  rb_root_t mm_rb;
  ...
  atomic_t mm_users;
  atomic_t mm_count;

  struct list_head mmlist;
  ...
};
  • 虚拟内存区域(VMA)
struct vm_area_struct {
  struct mm_struct * vm_mm;  //内存描述符
  unsigned long  vm_start;   //区域的首地址
  unsigned long vm_end;      //区域的尾地址
  struct vm_area_struct * vm_next; //VMA链表
  pgrot t_vm_page_prot;   //访问控制权限
  unsigned long vm_flags;   //保护标志位和属性标志位
  struct rb_node_ vm_rb;   //VMA的红黑树结构
  ...
  struct vm_operations_struct * vm_ops; //相关的操作表
  struct file * vm_file; //指向被映射的文件的指针
  void * vm_private_data; //设备驱动私有数据,与内存管理无关。
}

缺页中断

  • 当一个进程发生缺页中断的时候,进程会陷入内核态,执行以下操作:
  • 检查要访问的虚拟地址是否合法
  • 查找/分配一个物理页
  • 填充物理页内容
  • 建立映射关系(虚拟地址到物理地址)

内存分配原理

  • 从操作系统角度来看,进程分配内存有两种方式,分别由两个系统调用完成: brkmmap(不考虑共享内存)
  • brk是将数据段(.data)的最高地址指针_edata往高地址推
  • mmap是在进程的虚拟地址空间中(堆和栈中间,称为文件映射区域的地方)找一块空闲的虚拟内存
  • 这两种方式分配的都是虚拟内存,没有分配物理内存。在第一次访问已分配的虚拟地址空间的时候,发生缺页中断,操作系统负责分配物理内存,然后建立虚拟内存和物理内存之间的映射关系。
  • 在标准C库中,提供了malloc/free函数分配释放内存,这两个函数底层是由brk, mmap, munmap这些系统调用实现的

参考

  • 内存寻址之段页存储机制分析
  • Linux内存管理
  • 浅谈Linux内存管理
  • 内存分配的原理
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux内存管理是一个复杂而强大的系统级概念,它由多个组件和机制组成,以确保内存的有效使用和保护。以下是Linux内存管理的一些关键方面: 1. 内存分区:Linux将物理内存划分为多个分区或区域,每个分区都有不同的用途。例如,内核内存(用于操作系统代码和数据)、交换空间(用于将不再使用的内存页面交换到磁盘上的交换分区)、缓存空间(用于存储最近使用的页面,以便快速访问)等。 2. 页帧管理:页帧是内存管理的关键概念,它代表虚拟内存地址到物理内存地址的映射。Linux使用页帧来处理内存访问,确保正确地寻址和访问物理内存。 3. 交换(Swap):当系统内存不足时,Linux会使用交换机制将不再活跃的进程或数据交换到交换分区,以便为需要更多内存的进程腾出空间。 4. 缓存(Cache):Linux使用缓存来优化内存使用。缓存用于存储最近使用的页面,以便快速访问,从而减少访问时间并提高性能。 5. 请求调度(Request Scheduling):Linux使用请求调度算法来决定将内存分配给哪个进程。这些算法考虑了许多因素,如进程优先级、时间片、系统负载等。 6. 内存管理工具:Linux提供了一组工具,如free、top、vmstat等,用于监视和管理内存使用情况。这些工具可以帮助管理员和开发者了解系统的内存状态,并采取适当的措施来优化内存使用。 总的来说,Linux内存管理机制涉及多个组件和算法,旨在确保系统的稳定性和性能。它通过精细的调度、缓存和交换策略来管理内存资源,以确保在各种负载条件下提供最佳的性能和可用性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值