• 操作系统内存管理-01分段


    前言

    本文讲述本文博主在学习80386window下段式内存管理

    内存管理往往需要软硬件结合进行管理,CPU定制一套官方规范,要求操作系统按要求实现某些操作即可。本文所述的CPU 分段规范 在intel 第三卷 第三章。

    在这里插入图片描述

    分段概述

    我们知道每一个程序会被赋予一个"虚拟内存",在进行实际对内存写入的时候会进行一次转物理地址转化在进行操作。

    下图在来自intel手册,这个转化分为两个部分:段地址转化线性地址(分段机制),线性地址通过页表转化物理地址(分页机制)。
    在这里插入图片描述
    当然分页机制intel并没有做强制要求开启,但是分段逻辑被强制开启了。分页逻辑没有开启那么分段逻辑转化的线性地址就是物理地址。

    intel把内存划分很多个区域每个区域我们把它视为一个段,

    在这里插入图片描述
    将虚拟内存的段转化为物理的段地址。

    每个段都有相关可读可写等属性,这些属性被放入一个叫 段描述符(segment descriptor) 的数据结构中,并且数据结构还承担虚拟内存中的段转化为物理地址中段地址。

    在这里插入图片描述
    段描述表一般存于两个寄存器中GDTRLDTR。并且这两个寄存器存放的是直接物理地址中的表位置,不然的话会产生先有鸡还是现有蛋问题。

    我们可以用windbg调试内核输出相关寄存器

    //windbg相关命令
    //查看 gdtr寄存器
    r gdtr
    //查看 ldtr寄存器
    l ldtr
    //查看当前处理器信息 https://en.wikipedia.org/wiki/Processor_Control_Region
    !pcr
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这里插入图片描述
    如果是非内核调试可以用

    我们可以使用dd命令单独查看这个指向的地址数据
    在这里插入图片描述
    或者使用dg 查看解析后的段描述符号结果
    在这里插入图片描述
    一些可视化工具的输出
    在这里插入图片描述
    看了上面的数据后我们看看段选择子(segment selector)概念。我们知道GDTR存储一个表,里面有很多段描述符,而一个程序里面有很多段我怎么知道这个段跟哪个段描述符相关呢?这关系映射又是靠一个段选择子的数据结构维护的。段选择子必须加载到一个段寄存器中才能使用(这也导致了使用段机制一些缺陷,所以很多操作系都弱化了段的其中一个原因)。

    系统预置的段寄存器如下:
    CS SS DS ES FS GS

    他们存储的数据结构可以存储了相关GDTR表中索引,当然为了加速intel在这些寄存器末尾额外补充了一个64长度的数据用于缓存段描述符(也是64位),当然这部分存储空间对开发者不可见。也有称为影子寄存器或者称为段寄存器不可见部分。

    在这里插入图片描述
    上图来自intel手册。visible part 大小为16位也就是程序员可以操作的,hidden part就是影子寄存器。

    其数据结构如下:

    在这里插入图片描述

    第0位-第1位 作为权限位 用于权限请求位。
    第2位 表示你要去GDTR 还是LDTR查找表。
    第15-3位 作为GDT的下标,一共13位共计8192项。

    举个例子:
    在这里插入图片描述
    我们距离如下三个段选择子:
    cs=0008
    ss=0010
    ds=0023

    
    void printSegmentSelector(int16_t ss) {
    
    	int index =ss>>3;
    	int TI =ss>>2 & 0x1;
    	int PRL = 0x3 & ss;
    
    	char out[100] = { 0 };
    	sprintf_s(out, "index=[%d] ti = [%d] prl=[%d]", index, TI, PRL);
    	printf("%s\r\n", out);
    }
    int main()
    {
    	
    	
    
    	printSegmentSelector(0x0008);
    	printSegmentSelector(0x0010);
    	printSegmentSelector(0x0023);
    	return 0;
    }
    	
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    输出
    index=[1] ti = [0] prl=[0]
    index=[2] ti = [0] prl=[0]
    index=[4] ti = [0] prl=[3]

    我们使用dg命令去查看对应段描述符解析结果
    在这里插入图片描述
    可见这三个段的范围是0-4G 也就是整个内存,可见分段已经被弱化。

    段描述符

    数据结构是一个64位大小,如下图所示(来自itnel手册)
    在这里插入图片描述

    重要字段:
    Base Address: 32位的基地址,由描述符的三个部分组成(由于历史因素要向后兼容才会将其分散)。
    Segment Limit: 20位的段内偏移,由描述符的两个部分组成(由于历史因素要向后兼容才会将其分散)。
    G :设置Segment Limit 粒度是字节为单位还是4kb为单位,1为4kb

    我们假设G为 4k为单位那么Segment Limit偏移大小就是0到4G。

    这里我们直接使用pchunter查看解析结果即可
    在这里插入图片描述

    参考链接

    现代操作系统内存管理到底是分段还是分页,段寄存器还有用吗?

    what-is-the-difference-between-linear-physical-logical-and-virtual-memory-addr

    how-are-segment-registers-involved-in-memory-address-translation

    GDT_Tutorial

    内核_段寄存器|段描述符|段选择子(详解)

    Linux 获取虚拟地址对应的物理地址

  • 相关阅读:
    react之Component存在的2个问题
    论文解读(SUGRL)《Simple Unsupervised Graph Representation Learning》
    Linux之防火墙
    电压跌落检测
    node_modules/XXX/index.js:XXX;XX ||= XXX?.[level];SyntaxError: Une
    网络编程、广播、组播、数据库sqlite3
    Dotar(zsh,tmux,vim,ag)
    解决mybatis用Map返回的字段全变大写的问题
    邀请报名|11月24日阿里云原生 Serverless 技术实践营 深圳站
    Docker基本管理
  • 原文地址:https://blog.csdn.net/qfanmingyiq/article/details/127847043