美文网首页
ULK之一内存寻址之分段

ULK之一内存寻址之分段

作者: 1哥 | 来源:发表于2021-11-13 18:32 被阅读0次

    1.背景

    有了前面主存的分段的设计原理的背景知识,对于这里提到的分段和分页会更好理解。
    X86处理器有三种不同内存地址:

    • 逻辑地址
    • 线性地址
    • 物理地址

    X86 地址转换

    image.png

    MMU(内存管理单元)

    • 一种硬件,包含分段管理单元电路和分页管理单元电路
    • 通过分段管理单元电路,负责将CPU 产生的逻辑地址转换成实际的物理内存地址
    • 通过分页管理单元电路,负责将线性地址转换成物理地址。

    2.分段的实现

    image.png
    分段概念
    • 将整个进程地址空间划分成单独的逻辑地址空间
      代码段,数据段,堆段,栈段

    分段的实现

    • 虚拟地址:{段号,段内偏移}
    • 段的表示
      段基地址 + 段长度
      检查访问的偏移与段长度,提供保护
    • 段描述符表
      映射段号到段的基地址和段长度

    分段在80x86的实现

    • 逻辑地址表示:{段描述符, 段偏移}
    • 段标识符(段选择符): 16 bit


      image.png
    字段 位数 用途
    index 13 bit 段选择
    TI 1bit GDT or LDT 指示器
    RPL 2bit 特权级别选择
    • 段描述符


      image.png
    image.png
    • 段表
      1)段描述符要么存在全局描述符表(GDT),要么局部述符表(LDT)
      2)存放在内存中的地址寄存器:gdtr or ldtr

    • 转换过程如图
      1)检查段选择符的TI,确定段描述符在哪个描述符表(LDT or GDT)
      2)计算段描述符的地址: 据1)确定的位置,计算地址=gdt or ldtr 的值 + index * 8;
      3)得到线性地址:线性地址 = 2)确定的段描述符的地址的base + offset;


      image.png
    • 处于性能考虑

      • 段选择符寄存器
        1)为了更容易更快速获取段选择符
        2)六个:cs, ds, ss, es, fs, gs
      • 不可编程的寄存器
        1)6个寄存器,对应6个段描述符
        2)每次fork和上下文切换时,设置段描述符寄存器到对应的不可编程CPU寄存器中,此时逻辑地址转换需要访问GDT或者LDT
        3)后续地址转换,逻辑地址转换不需访问GDT或者LDT,直接引用包含段描述符的CPU寄存器的值
        image.png

    3.分段在Linux 中的实现

    3.1 段结构

    • 2.6 的Linux 只在x86 下才使用分段
    • 段描述符
      1)所有段地址从0x0 开始,Linux 下,逻辑地址和虚拟地址相同
      2)用户进程段选择符,通过__USER_CS、__USER_DS
      3)内核进程段选择符,通过__KERNEL_CS、__KERNEL_DS


      image.png
    • CPL(CPU当前特权级别)
      1)由cs的段选择符的RPL 确定
      2)当CPL 改变时对于的段寄存器需要更新
      如CPL=3(用户 mode)时,ds必须包含用户数据段的段选择符
      如CPL=0(内核 mode)时,ds必须包含内核数据段的段选择符

    3.2 GDT

    • 每个CPU 对应一个GDT
    • 所有GDT 存放在cpu_gdt_table里
    • 所有的GDT地址和大小被存放在 cpu_gdt_descr数组
    • 这些符号定义在 文件arch/i386/kernel/head.S.
    DEFINE_PER_CPU(struct desc_struct, cpu_gdt_table[GDT_ENTRIES]);
    EXPORT_PER_CPU_SYMBOL(cpu_gdt_table);
    
    void __init cpu_init (void)
    {
            //省略部分代码
           memcpy(&per_cpu(cpu_gdt_table, cpu), cpu_gdt_table,
                  GDT_SIZE);
           cpu_gdt_descr[cpu].size = GDT_SIZE - 1;
           cpu_gdt_descr[cpu].address =
               (unsigned long)&per_cpu(cpu_gdt_table, cpu);
            //省略部分代码
    }
    
    • GDT layout
      18个 段描述符


      image.png

    相关文章

      网友评论

          本文标题:ULK之一内存寻址之分段

          本文链接:https://www.haomeiwen.com/subject/qfrezltx.html