IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 内存管理<原理篇>(二、地址空间和链接和装入) -> 正文阅读

[嵌入式]内存管理<原理篇>(二、地址空间和链接和装入)

2.1 一种存储器的抽象:地址空间

2.1.1 地址空间概念

要使多个应用程序同时处于内存中并且不互相影响,需要解决两个问题:保护和重定位。

如果按上一节课的IBM 360的解决方案:

保护解决方案:给内存块标记上一个保护键,当进程访问内存的时候,进程的键和内存块的键相互比较。

重定位解决方案:通过在程序被装载时重定位程序来解决,缺点是缓慢且复杂。

所以大佬们创造了一个新的存储器抽象:地址空间。就像进程的概念创造了一类抽象的CPU以运行程序一样,地址空间为程序创造了一种抽象的内存。地址空间是一个进程可用于寻址内存的一套地址集合。

每个进程都有一个自己的地址空间,并且这个地址空间独立于其他进程的地址空间(除了在一些特殊情况下进程需要共享它们的地址空间外)

比较难的是给每个程序一个自己独有的地址空间,使得一个程序中的地址28所对应的物理地址与另一个程序中的地址28对应的物理地址不同。

下面介绍一种之前很常见的方法,基址寄存器和界限寄存器。

2.1.2 基址寄存器与界限寄存器

我们需要确保每个进程都有一个单独的内存空间。单独的进程内存空间可以保护进程而不互相影响,这对于将多个进程加到内存以便并发执行来说至关重要。

那我们怎么确定一个进程的合法地址范围呢???并且确保该进程只能访问自己合法地址范围???

其实方法也很简单,通过两个寄存器,一个是基地址寄存器,一个是界限寄存器。

基地址寄存器:最小的合法的物理内存地址。

界限地址寄存器:指定了范围的大小。

在这里插入图片描述

如图所示,进程2的基地址为300040,界限寄存器为120900,那么进程2的合法访问地址的范围是:300040–420939(含)。

那内存空间保护是怎么实现的呢?

用户进程在需要访问内存的时候,会把内存地址传给操作系统,并且会陷入操作系统,操作系统就会那到这个内存地址,并且与基地址寄存器和界限寄存器做比较,如果符合就可以访问,否则报错。

那怎么修改这两个寄存器的值?

只有操作系统可以通过特殊的特权指令可以修改,这也是防止被用户进程修改。

在这里插入图片描述

接下来,我们分析上一节说的那个多道程序运行的问题是否能在这个方案中解决。

在这里插入图片描述

上一节是问题是,执行到进程2的JMP 28这条语句就会出现问题,那现在执行到这条语句后,内存地址28会加上基地址寄存器16384 = 16412。所以这次访问正确。

但是也会存在缺点:每次访问内存都需要进行加法和比较运算。加法运算可能会比较耗时。

2.1.3 地址绑定

通常,我们写的代码都是文本文件,比如有.c或者.cpp,怎么从文件文件,变成计算机可执行的二进制文件?

这个详细的可以看我之前写的《编译链接》。这里只是简单提及一下。

代码是被编译链接之后形成一个二进制文件,其中代码中的地址通常是用符号表示(如变量count)。

编译链接就是把这些符号和一个地址相绑定。下面就是简单介绍一下编译链接的步骤:

  • 编译时:如果在编译时就已知道进程将在内存中驻留地址,那么就可以生成绝对代码。绝对代码的意思就是编译的时候就把变量的内存地址绑定,如果将来有变化,就需要重新编译。MS-DOS的.COM格式的程序就是在编译时绑定成绝对代码。
  • 加载时:如果在编译时并不知道进程将驻留在何处,那么编译器就应生成可重定位代码。最后的绑定会延迟到加载时才进行。
  • 执行时:如果进程在执行时可以从一个内存段移动另一个内存段,那么绑定应延迟到执行时才进行。

2.2 链接和载入

2.2.1 三种链接方式

  1. 静态链接:在程序运行之前,先将各目标模块及它们所需的库函数连接成一个完整的可执行文件(装入模块),之后不再拆开。
  2. 装入时动态链接:将各目标模块装入内存时,边装入边链接的链接方式。
  3. 运行时动态链接:在程序执行中需要该目标模块时,才对它进行链接。其优点是便于修改和更新,便于实现对目标模块的共享。

2.2.2 三种装入方式

  1. 绝对装入:在编译时,如果知道程序将放到内存中的哪个位置,编译程序将产生绝对地址的目标代码。装入程序按照装入模块中地址,将程序和数据装入内存。
  2. 静态重定位:又称可重定位装入。编译、链接后的装入模块的地址都是从0开始的,指令中使用的地址、数据存放的地址都是相对于起始地址而言的逻辑地址。可根据内存的当前情况,将装入模块装入到内存的适当位置。装入时对地址进行"重定位",将逻辑地址变换为物理地址(地址变换是装入时一次完成的)。
  3. 动态重定位:又称动态运行时装入。编译、链接后的装入模块的地址都是从0开始的。装入程序把装入模块装入内存后,并不会立即把逻辑地址转换为物理地址,而是把地址转换推迟到程序真正要执行时才进行。因此装入内存后所有的地址依然是逻辑地址。这种方式需要一个重定位寄存器的支持(或者存储在PCB中,这个需要看具体实现才知道)。

2.2.3 例子

例子我们就用哈工大老师讲的,感觉细节讲的很不错。

  1. 绝对装入

    // 源代码
    int main(int argc, char **argv)
    {
        ...
    }
    
    // 编译
    .text
        _entry:  // 入口地址
    	call _main
        call _exit
    _main:
    	...
        ret
            
    // 静态链接
    _entry:
    	call 40
        call xx
    _main: // 偏移是40
    	...
            
    // 绝对装入
    40  _main: mov[300], 0
        ...
        call xx
    0   call 40
    

    静态链接后,_main函数的地址是40,_entry的地址是0(这个是程序的入口函数,如果疑问程序入口地址为啥不是main,可以看我之前的文章)。

    绝对装入到内存中,_main函数自然要对应40物理地址,_entry也自然装载在0地址,但是每个程序都是都要装入到0地址的话,也没有那么多0地址给你装。

    所以这种方式只能适合单道程序环境。

  2. 重定位:修改程序中的地址(是相对地址)

    // 只有1000的位置空闲
    1040  _main:mov[300],0
          ...
          call xx
    1000  call 40
    // 重定位后
    1040  _main:mov[1300],0
          ...
          call xx
    1000  call 1040
    

    之前的绝对地址不灵活,如果内存中不可能每个程序都运行在0地址。所以需要找到一个空闲的内存,比如上面的例子:

    是1000的物理地址有空闲,所以程序需要重定位。重定位后每个地址都加上了1000的偏移。

    但是是什么时候完成重定位??

    编译时重定位:其实也是一种绝对装入,现在大部分应用在嵌入式系统。只能放在内存的固定位置

    载入时重定位:其实就是静态重定位,在载入的时候才重定位,一旦载入内存就不能移动了。

  3. 运行时重定位

    // 编译后的代码
    40  _main: mov[300], 0
        ...
        call xx
    0   call 40
    

    编译后的内存地址也是从0开始的,但是在进程运行的时候,PCB中有一个基地址base的变量(或者是重定位寄存器),保存了当前内存空闲的基地址,然后每执行一条指令都要从逻辑地址算出物理地址。也就是从加上base的偏移。

    基地址变量补充:如果当前进程是运行的时候,基地址变量需要保存到基地址寄存器中,这样翻译地址会加快。其他没有运行的进程,基地址变量是保存在PCB中

2.3 总结

这一篇也是在学习原理,学习了之前的一种存储器的抽象:地址空间。还有了解了链接和装入方式。其中对装入的方式还学习了哈工大老师的课程,讲的很不错,也更深入了解了装入方式。

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-05-05 11:35:52  更:2022-05-05 11:35:58 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/30 0:54:44-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码