静态连接、动态连接、静态重定位、动态重定位

重定位概念:程序在装入内存时将逻辑地址转换为物理地址。linux

重定位分类—程序员

静态重定位:在程序装入内存时就将逻辑地址转换为物理地址算法

                  优点:操做简单,不须要额外的机构或操做。编程

                  劣势:程序一旦装入后地址就不可再改变,程序也不能够再移动,不利于内存空间的有效使用。各个程序很难共享同一副本。微信

动态重定位:在程序运行期间,须要某一内存数据时才进行重定位。数据结构

                  优点:程序占用的内存空间可变,能提升内存的利用效率。比较容易实现不一样程序对同一副本的共同使用。函数

                  劣势:须要额外的硬件支持,增长了成本。性能


连接概念:各个模块独自编写、编译,而后由连接器将各个模块连接在一块儿,以解析模块间的访问和对库的访问。spa

静态连接:在程序装入内存以前完成连接工做,静态连接库里的指令最后都被包含在最终的EXE文件。操作系统

动态连接:动态连接库文件不会被包含在EXE文件里,EXE文件动态的引用动态连接库。

细分:

装载时动态连接:提早就已经知道程序要调用哪些模块,在编译时就在相应地方存放连接这些模块的信息,运行时对应去调用。

运行时动态连接:提早没法预知,彻底等运行时决定调用哪一个函数。


—————————————————————————————————————————————————————————

程序和数据装入内存时需对目标程序中的地址进行修改。这种把逻辑地址转变为内存的物理地址的过程叫重定位

对程序进行重定位的技术按重定位的时机可分为两种:静态重定位和动态重定位。

1.静态重定位

静态重定位是在目标程序装入内存时,由装入程序对目标程序中的指令和数据的地址进行修改,即把程序的逻辑地址都改为实际的地址。对每一个程序来讲,这种地址变换只是在装入时一次完成,在程序运行期间再也不进行重定位。

优势是:无需增长硬件地址转换机构,便于实现程序的静态链接。在早期计算机系统中大多采用这种方案。

缺点是:(1)程序的存储空间只能是连续的一片区域,并且在重定位以后就不能再移动。这不利于内存空间的有效使用。(2)各个用户进程很难共享内存中的同一程序的副本。

2.动态重定位

动态重定位是在程序执行期间每次访问内存以前进行重定位。这种变换是靠硬件地址变换机构实现的。一般采用一个重定位寄存器,其中放有当前正在执行的程序在内存空间中的起始地址,而地址空间中的代码在装入过程当中不发生变化。

此工做由硬件地址映射机制来完成。硬件支持,软硬件结合完成) 硬件上须要一对寄存器的支持。程序放在不连续的实际物理空间中,要进行逻辑地址到物理地址的转换,实现动态重定位通常须要页式存储管理,页式存储管理用的不是寄存器,使用的是称为page table(页表)的数据结构page table记录了全部逻辑地址到物理地址的转换信息,进程切换的时候须要冲洗硬件上的page table 。 

优势是:(1)程序占用的内存空间动态可变,没必要连续存放在一处。(2)比较容易实现几个进程对同一程序副本的共享使用。
缺点是:须要附加的硬件支持,增长了机器成本,并且实现存储管理的软件算法比较复杂。
如今通常计算机系统中都采用动态重定位方法。

连接:



1、分别编译与连接(Linking)

大多数高级语言都支持分别编译,程序员能够显式地把程序划分为独立的模块或文件,而后每一个独立部分分别编译。在编译以后,由连接器把这些独立的片断(称为编译单元)“粘接到一块儿”。(想一想这样作有什么好处?)

在C/C++中,这些独立的编译单元包括obj文件(通常的源程序编译而成)、lib文件(静态连接的函数库)、dll文件(动态连接的函数库)等。

静态连接方式:在程序执行以前完成全部的组装工做,生成一个可执行的目标文件(EXE文件)。

动态连接方式:在程序已经为了执行被装入内存以后完成连接工做,而且在内存中通常只保留该编译单元的一份拷贝。

2、静态连接库与动态连接库

先来阐述一下DLL(Dynamic Linkable Library)的概念,你能够简单的把DLL当作一种仓库,它提供给你一些能够直接拿来用的变量、函数或类。

静态连接库与动态连接库都是共享代码的方式,若是采用静态连接库,则不管你愿不肯意,lib中的指令都被直接包含在最终生成的EXE文件中了。可是若使用DLL,该DLL没必要被包含在最终的EXE文件中,EXE文件执行时能够“动态”地引用和卸载这个与EXE独立的DLL文件。

采用动态连接库的优势:(1)更加节省内存;(2)DLL文件与EXE文件独立,只要输出接口不变,更换DLL文件不会对EXE文件形成任何影响,于是极大地提升了可维护性和可扩展性。

3、认识动态连接库

动态连接是相对于静态连接而言的。所谓静态连接是指把要调用的函数或者过程连接到可执行文件中,成为可执行文件的一部分。换句话说,函数和过程的代码就在程序的exe文件中,该文件包含了运行时所需的所有代码。当多个程序都调用相同函数时,内存中就会存在这个函数的多个拷贝,这样就浪费了宝贵的内存资源。而动态连接所调用的函数代码并无被拷贝到应用程序的可执行文件中去,而是仅仅在其中加入了所调用函数的描述信息(每每是一些重定位信息)。仅当应用程序被装入内存开始运行时,在Windows的管理下,才在应用程序与相应的DLL之间创建连接关系。当要执行所调用DLL中的函数时,根据连接产生的重定位信息,Windows才转去执行DLL中相应的函数代码。通常状况下,若是一个应用程序使用了动态连接库,Win32系统保证内存中只有DLL的一份复制品

动态连接库的两种连接方法:

(1) 装载时动态连接(Load-time Dynamic Linking):这种用法的前提是在编译以前已经明确知道要调用DLL中的哪几个函数,编译时在目标文件中只保留必要的连接信息,而不含DLL函数的代码;当程序执行时,调用函数的时候利用连接信息加载DLL函数代码并在内存中将其连接入调用程序的执行空间中(所有函数加载进内存),其主要目的是便于代码共享。(动态加载程序,处在加载阶段,主要为了共享代码,共享代码内存)

(2) 运行时动态连接(Run-time Dynamic Linking):这种方式是指在编译以前并不知道将会调用哪些DLL函数,彻底是在运行过程当中根据须要决定应调用哪一个函数,将其加载到内存中(只加载调用的函数进内存),并标识内存地址,其余程序也可使用该程序,并用LoadLibrary和GetProcAddress动态得到DLL函数的入口地址。(dll在内存中只存在一份,处在运行阶段)

上述的区别主要在于阶段不一样,编译器是否知道进程要调用的dll函数。动态加载在编译时知道所调用的函数,而在运行态时则必须不知道。

重定位————————————————————————————————————————————————

连续分配方式,是指为一个用户程序分配一个连续的内存空间

----- 可重定位分区分配

一、动态重定位的引入

在连续分配方式中,必须把一个系统或用户程序装入一连续的内存空间。若是在系统中只有若干个小的分区,即便它们的容量总和大于要装入的

程序,但因为这些分区不相邻接,也没法把该程序装入内存。这种不能被利用的小分区称为“零头”或“碎片”。

当内存中出现几个互不邻接的小分区,它们单独的容量不能知足做业的大小,但它们的容量总和大于做业的要求时,若想把做业装入,可采用的

一种方法是:将内存中的全部做业进行移动,使它们所有相邻接,这样,即可把原来分散的多个小分区拼接成一个大分区,这时就可把做业装入该区。

拼接(紧凑/紧缩)-- 这种经过移动内存中做业的位置,而后把原来多个分散的空闲小分区拼接成一个大分区的方法,称为“拼接”或“紧凑”。

所谓拼接是指移动内存(存储器)中全部已分配区到内存的一端,使原本分散的小空闲区连成一个大的空闲区。

---- 因为通过紧凑后的某些用户程序内存中的位置发生了变化,此时若不对程序和数据的地址加以修改(变换),则程序必将没法执行。

为此,在每次“紧凑”后,都必须对移动了的程序或数据进行重定位。

---- 拼接时机(何时拼接):

第一种方案是在某个分区回收时当即进行拼接,这样在内存中老是只有一个连续的空闲区。但因为拼接很费时间,拼接频率太高会使系统开销加大。

第二种方案是当找不到足够大的空闲区空闲区的总容量能够知足做业要求时进行拼接。拼接的频率比第一种要小得多,但空闲区的管理稍微复杂一些。

二、动态重定位的实现

---- 在动态运行时装入的方式中,做业装入内存后的全部地址都仍然是相对地址逻辑地址),将相对地址转换为物理地址的工做,被推迟到程序指令

真正执行时进行。为使地址的转换不会影响到指令的执行速度,必须有硬件地址变换机构的支持,即需在系统中增设一个重定位寄存器,用来存放

程序(数据)在内存中的起始地址。程序在执行时,真正访问的内存地址相对地址重定位寄存器中的地址相加而造成的。

---- 地址变换过程是在程序执行期间,随着对每条指令或数据的访问自动进行的,故称为动态重定位。

---- 当系统对内存进行了“紧凑”而使若干程序从内存的某处移至另外一处时,不需对程序作任何修改,只需用该程序在内存的新起始地址,去置换原来的

起始地址便可。

三、动态重定位分区分配算法

---- 动态重定位分区分配算法与动态分区分配算法基本上相同,差异仅在于:在这种分配算法中,增长了紧凑功能

---- 一般,在找不到足够大的空闲分区来知足用户需求时进行紧凑。

算法流程图以下:

 

四、分区的存储保护

---- 存储保护是为了防止一个做业有意或无心地破坏操做系统其余做业。经常使用的存储保护方法有界限寄存器存储保护键方法。

---- 采用界限寄存器方法实现存储保护又有两种方式:

1)上、下界寄存器方法。采用上、下界寄存器分别存放做业的结束地址开始地址。在做业运行过程当中,将每个访问内存的地址都同这两个

寄存器的内容进行比较。在正常状况下,这个地址应大于下界寄存器,且小于上界寄存器的内容,如超出这个范围便产生保护性中断。

2)基址、限长寄存器方法。采用基址和限长寄存器分别存放做业的起始地址做业的地址空间长度。看成业执行时,将每个访问内存的

相对地址和这个限长寄存器比较,若是超过了限长,则发出越界中断信号,并中止做业的运行。

---- 存储保护键方法是给每一个存储块分配一个单独的保护键,它至关于一把锁。存储块不一样于分区,一个分区由若干个存储块组成,每一个存储块

大小相同,一个分区的大小必须是存储块的整数倍。此外,进入系统的每一个做业也被赋予一个保护键,它至关于一把钥匙。

看成业运行时,检查钥匙和锁是否一致,若是两者不匹配,则系统发出保护性中断信号,并中止做业的运行。


————————————————————————————————————————————

咱们你们在编程过程当中对“连接”这个词并不陌生,连接所解决的问题便是将咱们本身写的代码和别人写的库集成在一块儿。连接能够分为静态连接与动态连接,下文将分别讲解这两种方式的特色与其区别。
  1. 静态连接
    • 特色在生成可执行文件的时候(连接阶段),把全部须要的函数的二进制代码都包含到可执行文件中去。所以,连接器须要知道参与连接的目标文件须要哪些函数,同时也要知道每一个目标文件都能提供什么函数,这样连接器才能知道是否是每一个目标文件所须要的函数都能正确地连接。若是某个目标文件须要的函数在参与连接的目标文件中找不到的话,连接器就报错了。目标文件中有两个重要的接口来提供这些信息:一个是符号表,另一个是重定位表
    • 优势:在程序发布的时候就不须要的依赖,也就是再也不须要带着库一块发布,程序能够独立执行。
    • 缺点:
      1. 程序体积会相对大一些。
      2. 若是静态库有更新的话,全部可执行文件都得从新连接才能用上新的静态库。
  2. 动态连接
    • 特色: 在编译的时候不直接拷贝可执行代码,而是经过记录一系列符号和参数,在程序运行或加载时将这些信息传递给操做系统,操做系统负责将须要的动态库加载到内存中,而后程序在运行到指定的代码时,去共享执行内存中已经加载的动态库可执行代码,最终达到运行时链接的目的。
    • 优势: 多个程序能够共享同一段代码,而不须要在磁盘上存储多个拷贝。
    • 缺点: 因为是运行时加载,可能会影响程序的前期执行性能。
           上面的文章屡次提到库(lib)这个概念,所谓的库 是一些功能代码通过编译链接后的可执行形式。
             你们在Windows平台上见到的.dll文件和linux平台下so动态库都输入库。
             库也有静态lib和动态lib之分:
      1. 静态lib将导出声明和实现都放在lib中。编译后全部代码都嵌入到宿主程序。
      2. 动态lib至关于一个h文件,是对实现部分(.dll文件)的导出部分的声明。编译后只是将导出声明部分编译到宿主程序中,运行时候须要相应的dll文件支持。