我的博客:startcraft
保护模式
保护模式是从80386时代引入的,之前的8086只有实模式,实模式有1MB的寻址空间(2 ^ 20),而保护模式有4GB的寻址空间,且支持分页等操作
保护模式对内存段的访问做了限制,对于内存段的性质和允许的操作给出了定义,所以每一个段有一个段描述符,占8个字节,段描述符有很多,只能放在内存里,其中最重要的全局段描述符表(GDT),GDT的相关信息存在cpu的寄存器GDTR中
GDTR有48位,其中0 - 15位表示GDT的边界,也就是GDT段的大小,16 - 47位这32位存的是GDT的基地址,所以GDT段的大小最多为65536字节,也就是可以存8192个段描述符
也就是我们通过GDTR寄存器获取GDT的位置和大小,然后GDT中存储了所有段描述符
cup加电后工作在实模式,而进入保护模式后就需要GDT和GDTR,所以我们需要在进入保护模式之前就设置好GDT和GDTR,但是实模式只有1MB的寻址空间,所以一开始的GDT只能放在这里,进入保护模式后还可以更改
从实模式进入保护模式只需要修改CR0寄存器的第0位,为0表示实模式,为1表示保护模式
具体的分段策略
分段是cpu为了兼容以前的程序所保留的,所以我们一般绕过分段直接用分页技术,而绕过分段的方法就是把4GB的空间分成一段,整个虚拟地址的其实位置是0,长度为4G,这样偏移地址和段机制处理后的地址是同一个数
当然并不是给所有的段相同的描述符,数据段和代码段是不同的描述符
段描述符的结构
参考: https://blog.csdn.net/longintchar/article/details/50489889
看这个,就不搬运了
代码实现
include/gdt.h
#ifndef INCLUDE_GDT_H_
#define INCLUDE_GDT_H_
#include "types.h"
// 全局描述符类型
typedef
struct gdt_entry_t {
uint16_t limit_low; // 段界限 15 ~ 0
uint16_t base_low; // 段基地址 15 ~ 0
uint8_t base_middle; // 段基地址 23 ~ 16
uint8_t access; // 段存在位、描述符特权级、描述符类型、描述符子类别
uint8_t granularity; // 其他标志、段界限 19 ~ 16
uint8_t base_high; // 段基地址 31 ~ 24
} __attribute__((packed)) gdt_entry_t;
// GDTR
typedef
struct gdt_ptr_t {
uint16_t limit; // 全局描述符表限长
uint32_t base; // 全局描述符表 32 位基地址