Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

逻辑/线性/物理地址

定义

  • 逻辑地址

    • 是一个程序所看到和使用的地址空间,是逻辑意义上的存在,一般用段内偏移量表示
    • 需要经过分段转换
      • 实模式下:段基地址 + 段内偏移
      • 保护模式下:段选择符 + 段内偏移
    • 本质上还是运用了基地址,这样不同程序的代码运行不会冲突
  • 线性地址

    • 逻辑地址经过分段转换,就变成线性地址,是平坦的
    • 如果不使用分页,那么这就是物理地址
  • 物理地址

    • 如果使用了分页,那线性地址要经过分页转换,才会最终变成物理地址

分页

  • 【问题】分段在保护模式中解释过了,那分页又是什么?

    • 分页就是指: 按页为单位进行映射。
    • 对于连续的线性地址可以映射到不连续的物理内存上。
  • 存在分页时,线性地址不能直接到物理内存中找,而是要先经过如下路径

  • 处理线性地址,划分为

    • 高10位

      • 准备用来生成页目录的单项地址偏移,进入下一步
    • 中间10位

      • 准备用来生成页表的单项地址偏移,进入下下步
    • 低12位

      • 终极偏移量,留到最后使用
      • 由于2 ^ 12 = 4KB
      • 所以可用来在某个4KB的页里面准确定位
    • 【问】这里为什么是10位作为偏移?

    • 【答】10位如果左移2位,就能生成12位的真正偏移地址,地址跨度是4个字节,所以后面每一项的大小为4字节

  • 转战到页目录

    • 要先从CR3寄存器拿到基地址

      • 取高20位作为页目录的高位拼接地址
    • 页目录总共有 2 ^ 10 = 1024 项

    • 每一项大小为4字节32位,取出

      • 高20位
        • 作为页表的高位拼接地址,进入下一步
    • 所以页目录总占用空间为 1024 * 32bit = 4KB

    • 【问】这里为什么是高20位作为高位拼接地址?

    • 【答】高20位在左边,右边再拼接上一步的12位真正偏移地址,刚好就能组成32位地址(其实这种拼接 等价于 “完整的32位”与“12位” 做一次加法运算)

  • 转战到页表

    • 上一步已经拿到页表的高位拼接地址
    • 页表也是有 2 ^ 10 = 1024 项
    • 每一项大小也是4字节32位,取出
      • 高20位
        • 作为页的高位拼接地址,后面接上12位的终极偏移量,到物理内存中寻找页
    • 所以页表总占用空间为 1024 * 32bit = 4KB
  • 这么说来,页目录和页表也是页(大小为4KB)

  • 总结下来,1024项的页目录,每一项分别指向一个1024项的页表,每一项页表可以指定20位的高位拼接地址(也就是物理内存中的单个页的起始地址)

【猜想】这里的门道可能很大,因为可以让不同的线性地址指向同一个物理地址,2(2 ^ 10) * (2 ^ 10) 能得出 2 ^ 20个坑位(即1048576个页面),而这些坑位可以拿物理内存中的20位高位拼接地址去随便填,甚至是重复?

【猜想2】同样,不同程序中同一个虚拟地址也可以指向不同的物理地址?

查了下资料,通过改变段寄存器实现,貌似在分段生成线性地址时有GDTR和LDTR这两个概念

例子:在linux内核中,就有四种段选择器,分别是

_USER_CS 用户态的代码
_USER_DS 用户态的数据
_KERNEL_CS 内核态的代码
_KERNEL_DS 内核态的数据

代表用户态和内核态,可以用同样的地址,二者会被分段机制分配到不同的线性地址上

由于大部分Linux用户态程序不使用LDT局部描述符表,所以内核定义一个默认的LDT供大部分进程共用,然而,在某些情况下,进程可能需要设置它们自己的LDT