Linux GDT全局描述符表-草图示之

 

1. 段描述符表寄存器

系统设置的一个独立寄存器,用于存储段描述符表的内存地址和表的范围,就是表有多大,访问时不能超出表的范围。

2. 段描述表

系统中的一个数组,存储了很多段描述符,根据段寄存器存储的索引(即书上说的段选择子,忒太理解)确定内存段在段描述符表中的位置,进而取得段描述符信息,再根据段描述符信息的段基地址和段界限,得到内存段的内存地址。

段寄存器存储的索引,是在保护模式情况下,如果在实模式下,段寄存器存储的仍然是段基址。

这个内存段的内存地址是线性地址,在未启用分页管理时,线性地址即为物理地址,在启用了分页管理时,则cpu根据分页的页表目录,查找页面项,即先查找一级页表,再查找二级页表,根据页表中页的基地址和页内偏移地址转换成物理地址。

 3. 段描述符

段描述符是系统中,用于表示各种内存段的数据结构,包含的信息:内存段基地址,段内偏移界限(不能超过一个段的最大内存范围),段的属性包括很多如:段类型,内存段?代码段?堆栈段?系统的段类型什么陷阱,门等,不细究。

4. 段寄存器存储的索引(即书上说的段选择子,忒太理解)

段选择子就是段描述符在段描述符表中的索引值,段选择子,难听也不好理解。

最后总结:

cpu根据全局描述符表寄存器得到全局描述符表的内存地址,再根据段寄存器中的选择子即索引定位到段描述符,进而获取段描述符中的端基址和段界限,最后通过页管理的规则,转换到真实的物理地址。

cpu拿到的地址一般是虚拟地址或者说是偏移地址,比如程序里的4G进程空间中的任意一个地址,即4G内存空间中的地址,0到4G,cpu拿着这个偏移地址去查找它的段描述符,根据上述规则进而转换到物理地址。

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
首先,需要了解一下全局描述符(Global Descriptor Table,GDT)是什么。GDT是在操作系统启动时由CPU初始化的一个数据结构,用于管理内存段的访问权限。操作系统可以通过修改GDT来控制内存的访问权限和保护。 在Linux系统中,GDT的初始化是在setup.s文件中完成的。下面是setup.s文件中初始化GDT的代码和注释: ``` /* * 初始化GDT */ init_gdt: lgdt gdtr /* 加载GDT寄存器 */ /* GDT项0:空描述符 */ gdt_null: .word 0, 0 .byte 0, 0, 0, 0 /* GDT项1:内核代码段描述符 */ gdt_code: .word 0xffff, 0 /* 段界限 */ .byte 0, 0, 0x9a, 0xcf /* 基地址为0,代码段可读可执行,DPL为0,系统段,粒度为4KB */ /* GDT项2:内核数据段描述符 */ gdt_data: .word 0xffff, 0 /* 段界限 */ .byte 0, 0, 0x92, 0xcf /* 基地址为0,数据段可读可写,DPL为0,系统段,粒度为4KB */ /* GDT项3:用户代码段描述符 */ gdt_user_code: .word 0xffff, 0 /* 段界限 */ .byte 0, 0, 0x9a, 0xcf /* 基地址为0,代码段可读可执行,DPL为3,用户段,粒度为4KB */ /* GDT项4:用户数据段描述符 */ gdt_user_data: .word 0xffff, 0 /* 段界限 */ .byte 0, 0, 0x92, 0xcf /* 基地址为0,数据段可读可写,DPL为3,用户段,粒度为4KB */ /* GDT项5:TSS描述符 */ gdt_tss: .word 0x67, 0 /* 段界限 */ .byte 0, 0, 0x89, 0x40 /* 基地址为tss_entry,DPL为0,系统段,粒度为1B */ .word 0 /* tss_entry的低16位 */ .byte 0 /* tss_entry的第24位 */ .byte 0x00 /* tss_entry的第32位 */ .word 0 /* tss_entry的高16位 */ /* GDT项6:LDT描述符 */ gdt_ldt: .word 0, 0 /* 段界限 */ .byte 0, 0, 0x82, 0x40 /* 基地址为ldt,DPL为0,系统段,粒度为1B */ /* GDT项7:TSS段 */ gdt_tss_entry: .word 0, 0 /* 填充 */ .word 0x10, 0 /* ss0 */ .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 填充 */ /* GDT项8:LDT段 */ gdt_ldt_entry: .word 0, 0 /* 填充 */ .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 填充 */ /* GDT项9:TSS段 */ gdt_tss2_entry: .word 0, 0 /* 填充 */ .word 0x10, 0 /* ss0 */ .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 填充 */ /* GDT项10:LDT段 */ gdt_ldt2_entry: .word 0, 0 /* 填充 */ .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 填充 */ /* GDT项11:TSS段 */ gdt_tss3_entry: .word 0, 0 /* 填充 */ .word 0x10, 0 /* ss0 */ .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 填充 */ /* GDT项12:LDT段 */ gdt_ldt3_entry: .word 0, 0 /* 填充 */ .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 填充 */ .fill 16 * 8 - (. - init_gdt) /* 填充到16字节 */ /* GDT */ gdt: .word 16 * 8 - 1 /* GDT长度 */ .long gdt /* GDT地址 */ /* GDT寄存器 */ gdtr: .word 16 * 8 - 1 /* GDT长度 */ .long gdt /* GDT地址 */ ``` 上述代码中,首先使用`lgdt`指令将GDT的长度和地址加载到GDT寄存器中。然后,定义了一些GDT项,包括空描述符、内核代码段描述符、内核数据段描述符、用户代码段描述符、用户数据段描述符、TSS描述符和LDT描述符。其中,每个GDT项包括段界限、基地址、段属性等信息。 最后,定义了一个GDT数组`gdt`,将GDT长度和地址存储在`gdtr`寄存器中,完成了GDT的初始化。 需要注意的是,上述代码只是一个简化的示例,实际的GDT项可能更加复杂,具体的实现方式也可能因操作系统的不同而有所差异。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

种菜的

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值