C语言内存管理精讲1---虚拟地址到物理地址的映射

目录

1、程序在计算机中到底是如何运行的?

CPU指令

2、虚拟内存

中间层思想

3、虚拟地址空间以及编译模式

CPU的数据处理能力

1) 16位CPU

2) 32位CPU

3) 64位CPU

编译模式

32位编译模式

64位编译模式

开启64位编译模式

4、C语言内存对齐,提高寻址效率

改变对齐方式

5、内存分页机制,完成虚拟地址的映射

内存分页机制

关于页的大小

根据页进行映射

6、分页机制究竟是如何实现的?

直接使用数组转换

使用一级页表

使用两级页表

使用多级页表

7、MMU部件以及对内存权限的控制 

MMU

内存权限控制


1、程序在计算机中到底是如何运行的?

程序是保存在硬盘中的,要载入内存才能运行,CPU也被设计为只能从内存中读取数据和指令

对于CPU来说,内存仅仅是一个存放指令和数据的地方,并不能在内存中完成计算功能,例如要计算 a = b + c,必须将 a、b、c 都读取到CPU内部才能进行加法运算。为了了解具体的运算过程,我们不妨先来看一下CPU的结构。

CPU是一个复杂的计算机部件,它内部又包含很多小零件,如下图所示:

运算单元

        CPU的大脑,负责加减乘除、比较、位移等运算工作,每种运算都有对应的电路支持,速度很快。

寄存器(Register)

        CPU内部非常小、非常快速的存储部件,它的容量很有限,对于32位的CPU,每个寄存器一般能存储32位(4个字节)的数据,对于64位的CPU,每个寄存器一般能存储64位(8个字节)的数据。为了完成各种复杂的功能,现代CPU都内置了几十个甚至上百个的寄存器,嵌入式系统功能单一,寄存器数量较少。

        我们经常听说多少位的CPU,指的就是寄存器的的位数。现在个人电脑使用的CPU已经进入了64位时代,例如 Intel 的 Core i3、i5、i7 等。 

        在CPU内部为什么又要设置缓存呢?虽然内存的读取速度已经很快了,但是和CPU比起来,还是有很大差距的,不是一个数量级的,如果每次都从内存中读取数据,会严重拖慢CPU的运行速度,CPU经常处于等待状态,无事可做。在CPU内部设置一个缓存,可以将使用频繁的数据暂时读取到缓存,需要同一地址上的数据时,就不用大老远地再去访问内存,直接从缓存中读取即可。

大家在购买CPU时,也会经常关心缓存容量,例如 Intel Core i7 3770K 的三级缓存为 8MB,二级缓存为 256KB,一级缓存为 32KB。容量越大,CPU越强悍。        

CPU指令

        要想让CPU工作,必须借助特定的指令,例如 add 用于加法运算,sub 用于除法运算,cmp 用于比较两个数的大小,这称为CPU的指令集(Instruction Set)。我们的C语言代码最终也会编译成一条一条的CPU指令。不同型号的CPU支持的指令集会有所差异,但绝大部分是相同的。

我们以C语言中的加法为例来演示CPU指令的使用。假设有下面的C语言代码:

int a = 0X14, b = 0XAE, c;
c = a + b;

在VS2010 Debug模式下生成的CPU指令为:

mov  ptr[a], 0X14
mov  ptr[b], 0XAE
mov  eax, ptr[a]
add  eax, ptr[b]
mov  ptr[c], eax

mov 和 add 都是CPU指令:
1) mov 用来将一个数值移动到一个存储位置。这个数值可以是一个常数,也可以在内存或者寄存器上;这个存储位置可以是寄存器或者内存。

第一条指令中,ptr[a]表示变量 a 的地址,0X14是一个数值,mov ptr[a], 0X14表示把数值 0X14 移动到 ptr[a] 指向的内存,也就是给变量 a 赋值。第二条指令与此类似。

第三条指令中,eax是寄存器的名字,该寄存器常用在加法运算中,用来保存某个加数或运算结果,mov eax, ptr[a]表示把变量 a 的值移动到寄存器 eax 中。

第五条指令表示把寄存器 eax 的值移动到变量 c 中,此时 exa 中的值为 a、b 相加的和。

2) add 用来将两个数值相加,这两个数值可以在寄存器或者内存中,add 会将相加的结果放在第一个数所在的位置。第四条指令add  eax, ptr[b]表示把 eax 和 ptr[b] 中的数值相加,并把结果放在 eax 中。

2、虚拟内存

        在C语言中,指针变量的值就是一个内存地址,&运算符的作用也是取变量的内存地址,请看下面的代码:

#include <stdio.h>
#include <stdlib.h>
int a = 1, b = 255;
int main(){
    int *pa = &a;
    printf("pa = %#X, &b = %#X\n", pa, &b);
    system("pause");
    return 0;
}

        代码中的 a、b 是全局变量,它们的内存地址在链接时就已经决定了,以后再也不能改变,该程序无论在何时运行,结果都是一样的。

        那么问题来了,如果物理内存中的这两个地址被其他程序占用了怎么办,我们的程序岂不是无法运行了?

        幸运的是,这些内存地址都是假的,不是真实的物理内存地址,而是虚拟地址。虚拟地址通过CPU的转换才能对应到物理地址,而且每次程序运行时,操作系统都会重新安排虚拟地址和物理地址的对应关系,哪一段物理内存空闲就使用哪一段。如下图所示: 

        例如,上面代码中变量 a 的地址是 0X402000,第一次运行时它对应的物理内存地址可能是 0X12ED90AA,第二次运行时可能又对应 0XED90,而我们的程序不需要关心这些,这些繁杂的内存管理工作交给操作系统处理即可。

中间层思想

在计算机中,为了让操作更加直观、易于理解、增强用户体验,开发者经常会使用一件法宝——增加中间层,即使用一种间接的方式来屏蔽复杂的底层细节,只给用户提供简单的接口。虚拟地址是使用中间层的一个典型例子。

实际上,计算机的整个发展过程就是不断引入新的中间层:

  • 计算机的早期,程序都是直接运行在硬件之上,自己负责硬件的管理工作;程序员也使用二进制进行编程,需要处理各种边界条件和安全问题。
  • 后来人们不能忍受了,于是开发出了操作系统,让它来管理各种硬件,同时发明了汇编语言,减轻程序员的负担。
  • 随着软件规模的不断增大,使用汇编语言编程开始变得捉襟见肘,不仅学习成本高,开发效率也很低,于是C语言诞生了。C语言编译器先将C代码翻译为汇编代码,再由汇编器将汇编代码翻译成机器指令。
  • 随着计算机的发展,硬件越来越强大,软件越来越复杂,人们又不满足于使用C语言了,于是 C++、Java、C#、PHP 等现代化的编程语言诞生了。

3、虚拟地址空间以及编译模式

        所谓虚拟地址空间,就是程序可以使用的虚拟地址的有效范围。虚拟地址和物理地址的映射关系由操作系统决定,相应地,虚拟地址空间的大小也由

  • 40
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C语言是一种广泛应用于计算机科学和软件开发的编程语言。它具有强大的功能和灵活性,适用于开发各种类型的应用程序。 C语言专题精讲篇是一个对C语言进行深入学习和讲解的系列文章或课程。它汇总了C语言相关的重要知识点和技巧,旨在帮助学习者更好地理解和运用C语言。 这个专题中的笔记涵盖了C语言的各个方面,包括基本语法、数据类型、运算符、流程控制、函数、数组、指针、结构体、文件操作等。通过系统性的学习和总结,这些笔记可以帮助学习者逐步掌握C语言的核心概念和常用技巧。 在这个专题中,学习者可以学到如何编写简单的C程序,如何使用变量和运算符进行计算,如何使用条件和循环语句控制程序流程,如何使用函数进行代码的模块化,如何使用数组和指针进行数据的处理,如何使用结构体组织复杂数据,如何进行文件的读写等等。 C语言专题精讲篇的目的是帮助学习者全面、深入地了解C语言的各个方面,并能够独立编写和调试简单到中等难度的C程序。通过反复实践和练习,学习者可以逐渐提高自己的编程能力,并为进一步学习更高级的编程语言打下坚实的基础。 总之,C语言专题精讲篇的笔记汇总是一份重要的学习资料,可以帮助学习者系统地学习和掌握C语言的基础知识和常用技巧,为他们未来的编程之路打下坚实的基石。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值