学习Windows程序设计也有一些时间了,为了记录自己的学习成果,以便以后查看,我希望自己能够坚持写下一系列的学习心得,对自己学习的内容进行总结,同时与大家交流。因为刚学习所以可能有的地方写不不正确,希望大家能够指出。

在学习了一定的Windows API后我决定进入到一些基础的学习,希望能够学习一些原理性的知识,能够做到知其然的同时知其所以然。为了达到这个目的,这段时间我学习了一些计算机的基础知识,在这写下这篇博客,总结一下。

在早期的16位8086CPU中我们使用段与段内的偏移偏移的方式寻址,得到的是真实的物理地址,当时的寄存器是16位而地址总线是20位,为了充分利用这些地址总线,Intel的工程师采用的是分段的方式,将段寄存器与通用寄存器中的数值通过地址加法器合成20位的地址,具体的合成方式为段地址 * 16 + 偏移地址,利用这种方式得到的都是真实的物理地址。8086系列的CPU之所以成为一个划时代的产物就是因为这种分段的思想,而这种思想一直沿用至今。但是8因为086CPU得到的都是真实的物理地址,所以在早期的程序设计中不得不详细考虑内存段的划分,有可能出现后一个程序将前一个程序的内存占用,这种方式非常不安全。

到32位CPU的诞生产生了一种新的寻址方式,80386CPU有32位地址总线,寻址区间为2的32次方为4GB。所以用32位的通用寄存器都可以访问4GB的内存空间,这个时候段寄存器不在存储段的首地址段,寄存器DS、CS、ES等寄存器中存储的是段选择子的索引。段选择子的概念出现在32位CPU中的保护模式中,在保护模式下,每个内存段都有一个64位的结构体用来描述这段内存的基本信息叫做段描述符,段描述符包括安全级别,段的基地址等信息,系统将所有内存段的64位描述符存储在一个段描述符表中,将段寄存器中的16位数据作为段描述符表的索引,称为选择子。为了管理段描述符表,80386引入两个寄存器一个是48位的GDTR(全局段描述符表寄存器)另一个是16位的LDTR(局部段描述符表寄存器)分别指向GDT(全局段描述符表)和LDT(局部段描述符表)。

GDT(Global Descriptor Table)包含系统中所有任务都可用的段描述符,通常包含描述操作系统所使用的代码段、数据段和堆栈段的描述符及各任务的LDT段等;全局描述符表只有一个。而LDTR(Local Descriptor Table)80386处理器设计成每个任务都有一个独立的LDT。它包含有每个任务私有的代码段、数据段和堆栈段的描述符,也包含该任务所使用的一些门描述符,如任务门和调用门描述符等。系统根据不同的任务切换不同的LDT。这样便于实现不同进程间内存的隔离。

为了确定所在段描述符的位置,段寄存器中的16位数据中只有13位表示段描述中的索引。第0位、第1位合起来表示程序的等级,第2位TI位用来表示在段描述符的位置;TI=0表示在GDT中,TI=1表示在LDT中。对于一个虚拟地址XXXX:YYYYYYYY首先判断XXXX中TI位的值,当TI = 0时表示的是全局段描述符表,这个时候首先利用GDTR中的值确定GDT的位置,然后直接取段寄存器中高13位的值作为索引在GDTR中查找得到相应的段描述符,由于段描述符包含段的基址、限长、优先级等各种属性,这就得到了段的起始地址;当TI等于1时表示的是局部的段描述符表,这个时候寻址变得相对比较复杂,第一步还是从GDTR获得GDT的位置,然后根据LDTR中的16位数值作为索引在GDT中查找LDT所在位置,然后才是根据XXXX中的高13位作为索引在LDT中查找得到相应的段描述符,由于段描述符包含段的基址、限长、优先级等各种属性,这就得到了段的起始地址。

这两种方式的步骤如下图:

在这里我们只是说得到线性地址并没有说得到内存地址,那么线性地址是否就是内存地址呢?可以是也可以不是,这取决于80386的分页机制是否被使用。

在早期的分段模式下,系统回收程序使用的内存可能会残留很小的内存碎片,导致任何程序都不能使用,为了解决这一问题,80386CPU提供了一种分页机制,系统将固定大小的内存块分为一页,在一页中在使用段分配的方式,每页的大小有系统决定,Windows系统的页大小为4KB。每页物理内存可以根据“页目录”和“页表”,随意映射到不同的线性地址上。这样,就可以将物理地址不连续的内存的映射连到一起,在线性地址上视为连续。而是否启用内存分页机制是由80386处理器新增的CR0寄存器中的位31(PG位)决定的。如果PG=0,则分页机制不启用,这时所有指令寻址的地址(线性地址)就是系统中实际的物理地址;当PG=1的时候,80386处理器进入内存分页管理模式,所有的线性地址要经过页表的映射才得到最后的物理地址。当每页大小为4KB时,我们得到的32位线性地址中高20位表示页号,低12位表示页中的偏移地址,这样通过页映射的方式我们可以将线性地址转化成对应的物理地址。

到这里我们可引出几个重要的概念:

1)32位机器中共有2中段描述符表,每个表中有2^13个表项,每个表项代表一个段首地址,而每一段的段内偏移最大为2^32B所以32位机器的理论寻址范围为2^13 * 2^1 * 2^32 = 2^(13 + 1 + 32) = 64TB;

2)系统为每个应用程序分配4GB的虚拟内存,并且由于保护模式的相关机制,这4GB的内存为每个应用程序独享

3)由于保护模式不同应用程序中相同的逻辑地址最终映射到不同的内存地址,所以在应用程序中我们不必过多操心程序所使用的内存被其他应用程序占用。

在Windows的保护模式中,将应用程序分级分为RING0到RING3,其中RING0的级别最高、GING3的级别最低,虽说分为4个级别但是实际上只使用了两个,Windows为了与其他CPU兼容,只使用2个,RING0主要是内核代码、RING3主要是用户代码,那是不是说RING3级别的代码就不能访问RING0级别的内存呢?这个自然也不是,Windows我们都知道Windows提供了一系列的API
,其中我们可以调用相应的API访问内核所在的内存,只是不能直接访问内核代码,也就是说不能直接用jmp指令访问内核代码,但是可以使用call指令调用,API对于程序员来说是不透明的。

Windows保护模式下主要机制有:

1)Windows提供不同安全级别,不同安全级别的代码访问内存的权限也不一样

2)不同进程的内存都是独立的,每个进程独享自己的4GB内存,不同进程即使在代码中使用相同的虚拟地址,系统也会映射到不同 的地址空间

Windows程序设计学习笔记(一)Windows内存管理初步的更多相关文章

  1. iOS学习笔记之ARC内存管理

    iOS学习笔记之ARC内存管理 写在前面 ARC(Automatic Reference Counting),自动引用计数,是iOS中采用的一种内存管理方式. 指针变量与对象所有权 指针变量暗含了对其 ...

  2. Windows程序设计学习笔记(1):一个简单的windows程序

    <Windows程序设计>(第五版)(美Charles Petzold著) #include<windows.h> LRESULT CALLBACK WndProc(HWND, ...

  3. 【cocos2d-x 3.x 学习笔记】对象内存管理

    内存管理 内存管理一直是一个不易处理的问题.开发人员必须考虑分配回收的方式和时机,针对堆和栈做不同的优化处理,等等.内存管理的核心是动态分配的对象必须保证在使用完成后有效地释放内存,即管理对象的生命周 ...

  4. JVM学习笔记一:内存管理

    参考资料 本文参考:<深入理解Java虚拟机>作者 周志明 知识产权归作者所有 走近java java组成部分:java语言.各平台虚拟机.Class文件结构.java api 类库.第三 ...

  5. Windows程序设计学习笔记(五)——菜单资源和加速键的使用

    菜单可能是Windows提供的统一用户界面中最重要的一种方式,菜单通常在标题栏的下一行显示,这一栏叫做菜单栏,菜单栏中的每一项称之为菜单项,菜单栏中的每一个菜单项在激活时会显现一个下拉菜单(也可以说是 ...

  6. Windows程序设计学习笔记(四)自绘控件与贴图的实现

    Windows系统提供大量的控件供我们使用,但是系统提供的控件样式都是统一的,不管什么东西看久了自然会厌烦,为了使界面更加美观,添加一些新的东西我们需要自己绘制控件. 控件在默认情况下并不进行自绘,如 ...

  7. windows程序设计学习笔记(一)

    windows里的变量类型 1.简单重定义windows变量 BOOL (TRUE FALSE) INT UINT(32位,4字节) LONG DWORD(32位,4字节) lParam,wParam ...

  8. 20145213《Java程序设计学习笔记》第六周学习总结

    20145213<Java程序设计学习笔记>第六周学习总结 说在前面的话 上篇博客中娄老师指出我因为数据结构基础薄弱,才导致对第九章内容浅尝遏止地认知.在这里我还要自我批评一下,其实我事后 ...

  9. python学习笔记--Django入门四 管理站点--二

    接上一节  python学习笔记--Django入门四 管理站点 设置字段可选 编辑Book模块在email字段上加上blank=True,指定email字段为可选,代码如下: class Autho ...

随机推荐

  1. com.sun.mail.smtp.SMTPSendFailedException: 553 Mail from must equal authorized user

    1.错误描写叙述 553 Mail from must equal authorized user com.sun.mail.smtp.SMTPSendFailedException: 553 Mai ...

  2. Docker-py 的使用

    Docker SDK for Python A Python library for the Docker Engine API 具体文档这里,https://docker-py.readthedoc ...

  3. 增加tomcat的缓存

    起因是我做了一个批量压缩图片的功能,在服务器上跑这个功能的时候,发现服务器有警告.警告的内容大概如下.  XX....  to the cache because there was insuffic ...

  4. Solr集群搭建详细教程(二)

    注:欢迎大家转载,非商业用途请在醒目位置注明本文链接和作者名dijia478,商业用途请联系本人dijia478@163.com. 之前步骤:Solr集群搭建详细教程(一) 三.solr集群搭建 注意 ...

  5. 前端构建之gulp与常用插件(转载)

    原博主:幻天芒 原文地址:http://www.cnblogs.com/humin/p/4337442.html gulp是什么? http://gulpjs.com/ 相信你会明白的! 与著名的构建 ...

  6. Xamarin android spinner的使用方法

    <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android=&quo ...

  7. “乐”动人心--2017年10款最佳音乐类APP设计盘点

    在上下班的路上,听几首自己喜欢的音乐来打发无聊的等公交车和地铁的时间是现代年轻人的常态.音乐作为最能鼓动人心的"语言",也成为了人们在互联网生活里占比例最高的消费活动之一,一款好看 ...

  8. 了解数组中的队列方法,DOM中节点的一些操作

    队列的概念 栈是一种后进先出的结构,而队列是一种先进先出的结构.如银行排队,排在前面的人先办业务然后离开,后来的人站在最后.可以用队列的push()方法插入元素到队列的末尾,可以用shift()方法删 ...

  9. 2.sass变量、嵌套、混合(mixin)、继承拓展、@import、comment

    变量.嵌套.混合(mixin).继承拓展.@import.comment 变量的意义 在sass里我们可以定义多个变量来存放颜色.边框等等的样式,这样就可以在下面想要使用样式的时候使用变量了 这样的优 ...

  10. 添加MD5 密码加密

        编辑 /etc/grub/grub.conf 配置文件 password = 123456 password --md5 $5$H.........SS grub-crypt  --md5   ...