什么是可重定位代码

顾名思义,可重定位 就是 可以重新定位  的意思。

我们都知道,在写代码时,代码里的各种跳转代码/指令,
比如C语言里的goto,汇编里的jmp、jz等等,
它们都是跳到某一地址,然后在该地址继续往下执行代码 的意思,
我们写的代码时用的内存空间是逻辑空间,
但是代码在实际运行时,用到的却是货真价实的物理地址空间。

既然如此,那么在程序编译时,就需要有一个从逻辑地址空间物理地址空间的映射,如果这个映射做得不好,那么就会影响代码的正确执行。

打个比方,下面有一段代码:

    AAAA;(假设这里是代码的起始地址0)


    ...        (中间经略过了187个地址)


    XXXX;(假设这一行的逻辑地址是188)
circle:
    YYYY;(那么这一行就应该是189)
    ZZZZ;(190)
    MMM;(191)

goto circle;(又跳到circle,也就是地址为189那里)

因为操作系统给进程分配的内存的起始位置是无法确定的,

换句话说,也就是程序 在运行时实际的物理起始位置是不确定,所以不能在编译时就把地址给写死,否则,如果实际运行时物理空间起始位置与编译时写死的起始地址  不一致的话,程序就会出问题。

举个栗子,就拿上面的代码来讲,假设编译时,按照起始地址为0,给每一行代码都定死了地址(地址为右边括号里的数字),然而在实际运行这段程序时,系统可能给这个进程分配的 物理地址 起始地址为 2000,那么当代码执行到 goto circle;这一句时,本应该跳到的地址为2189,但是因为编译时已经把调转地址给写死了,实际却跳到地址189,至于这个189是哪一个进程的什么代码我们不知道,但可以肯定得是,这已经跳出了自己的物理空间,跳到别人的物理空间执行别人的代码去了,毫无疑问这样会出问题的。

为了解决这个问题,科学家们就把心思放在编译这一步骤上。

如果我们编译时,涉及  地址跳转,某个地址对应信息的读取、写入   等等之类与地址有关的操作,代码里所有地址都采用动态调整的方式,也就是可以根据操作系统实际给进程分配的  实际物理内存 的起始位置 ,而进行调整的话,那么代码就不会出错啦。

同样拿上面的例子来说,假设运行该代码时,系统给这个进程分配的 物理地址 起始位置为 2000,而代码自动根据这个起始位置调整自己的地址,那么代码实际上是:

    AAAA;(假设这里是代码的起始地址2000)


    ...        (中间经略过了187个地址)


    XXXX;(假设这一行的逻辑地址是2188)
circle:
    YYYY;(那么这一行就应该是2189)
    ZZZZ;(2190)
    MMM;(2191)

goto circle;(又跳到circle,也就是2189那里)

这样的话,代码的执行就不会出错啦。

这种可以使地址平移的代码就叫做可重定位代码,它是在加载的时候,也就是系统给进程确定了物理地址时,才生成绝对地址的。

重定位是由操作系统安排的。在装入程序前,系统会计算未使用的内存,然后将程序装入,并记下开始地址。在执行有相对地址的指令时,会将所有的地址加个刚才记下的开始地址,就叫重定位。它是实现多道程序在内存中同时运行的基础。

实际上使得代码可重定位方式有两种,分别是动态重定位与静态重定位

  1. 静态重定位:即在程序装入内存的过程中完成,是指在程序开始运行前,程序中的各个地址有关的项均已完成重定位,地址变换通常是在装入时一次完成的,以后不再改变,故成为静态重定位。
  2. 动态重定位:它不是在程序装入内存时完成的,而是CPU每次访问内存时 由动态地址变换机构(硬件)自动进行把相对地址转换为绝对地址。动态重定位需要软件和硬件相互配合完成。

这里不讲述第2种,能理解第1种就可以啦。