建设行网站修改电话,网站怎么做安全,老鹰网站建设,重庆有哪些做网站公司每一个不曾起舞的日子#xff0c;都是对生命的辜负。 磁盘结构/文件系统/软硬链接/动静态库前言一.磁盘结构1.1 磁盘的物理结构1.2 磁盘的存储结构1.3 磁盘的逻辑结构二.理解文件系统2.1 对IO单位的优化2.2 磁盘分区与分组2.3 分组的管理方法2.4 文件操作三.软硬链接3.1理解硬… 每一个不曾起舞的日子都是对生命的辜负。 磁盘结构/文件系统/软硬链接/动静态库前言一.磁盘结构1.1 磁盘的物理结构1.2 磁盘的存储结构1.3 磁盘的逻辑结构二.理解文件系统2.1 对IO单位的优化2.2 磁盘分区与分组2.3 分组的管理方法2.4 文件操作三.软硬链接3.1理解硬链接3.2 理解软链接3.3 理解.3.4 理解..四.动态库和静态库4.1 库的理解4.2 什么是静态库4.3 什么是动态库4.4 demo五.动静态库的加载理解5.1 加载静态库5.2 加载动态库前言
在之前文件的学习中一开始我们就提到了文件操作的本质是进程和被打开文件之间的关系那没有没打开的文件呢如果一个文件没有被打开该如何被OS管理呢
没有被打开的文件只能静静的放在磁盘上放着
磁盘上面有大量的文件而其中的大部分都是处于未被打开的状态一这些文件也需要被静态管理起来方便我们随时找到并打开操作系统对未打开文件的管理称为文件系统。就比如快递点的快递没有被取走但也有一一对应的取件码方便取走。那磁盘是如何管理的呢
一.磁盘结构
磁盘是我们计算机中唯一的一个机械结构。要理解操作系统如何对磁盘上的未打开文件进行管理首先我们需要对磁盘这个设备的物理结构、存储结构与逻辑结构进行理解然后再在此基础上理解操作系统对磁盘的管理方法。
1.1 磁盘的物理结构
总体来说硬盘结构包括 盘片、磁头、盘片主轴、控制电机、磁头控制器、数据转换器、接口、缓存等几个部份。 盘片 磁盘是按摞的也就是说一个磁盘有很多个盘片。 盘面 一个盘片有两个盘面。 磁头 每一面都有一个磁头。也就是说假如磁盘有五片那么就有十面也就有十个磁头。磁头和盘面是没有接触的是悬浮在盘面上的一旦盘面高速旋转磁头就会漂浮起来因此磁盘必须防止抖动否则磁头就会上下摆动给盘面刮花造成磁盘上的二进制数据丢失。 马达 盘片加电之后马达会使盘片逆时针告高速旋转此时的磁头会来回的左右摆动。因此马达设备可以控制磁头摆动与盘片旋转。 可以发现在磁盘内部也有自己的硬件电路硬件电路有硬件逻辑可以称之为磁盘本身的伺服系统即可以通过硬件电路组成伺服系统从而给磁盘发送二进制指令让磁盘定位或者寻址某个特定的区域从而读取磁盘上对应的数据。 我们的笔记本电脑装载的不是机械磁盘而是SSD。磁盘是我们计算机中唯一的一个机械结构。相对于其他设备而言由于磁盘是一个硬件结构外设所以硬盘访问相对较慢只是相对的因此操作系统就需要处理很多工作。虽然目前很少见到磁盘但在企业端磁盘依旧是主流由于SSD成本太高且有读写限制容易被击穿即便访问速度远超磁盘但也不能将磁盘完全替代。 注意磁盘封装性强可以拆开但是一旦拆开立即报废。
1.2 磁盘的存储结构 磁盘有很多片每片都有两面我们拿其中一面进行分析。 通过俯视观察一个面有无数个同心圆每圈都是一个磁道磁道的每一段都会存储大量的数据。磁盘寻址时基本单位不是bit更不是byte而是扇区一般的磁盘一个扇区的大小为512byte。如图一个小段就是扇区。 对于同心圆来说越向外的扇区面积越大但他们都是扇区也就是都是512byte为了保持存储大小相同因此越靠近圆心的扇区密度越大越远离圆心的扇区密度越小因此这样各个扇区存储的大小都是相同的。当然存在扇区大小不同的磁盘但这里不考虑
数据存储在扇区上。那么在一个面中我们怎么能够定位任意一个扇区呢 确认扇区首先需要确定扇区所在的磁道。设计时根据磁道距离圆心的远近将磁道进行了一一的编号磁头的来回摆动实际上就是寻找指定磁道的过程一旦找到磁头就会停在指定位置上不在摆动。确认磁道之后盘片的高速旋转与静止的磁头形成了相对运动此时盘片的旋转就是让磁头定位扇区的过程。盘片旋转越快效率越高。 但实际上磁盘不是成面存在的而是一摞磁盘。 可以看出柱面就是每一个盘面上同心同大小的磁道。柱面这个概念同样重要因为对于一摞磁盘它的所有的磁头都是连在一起共进退的因此在磁盘上查找数据时磁头不是在一面上去找而是拿着一摞磁头在所有面的磁道上去找。因此柱面这个概念就相当于确定了所有磁头所在的位置。 磁盘寻址的过程 磁盘在进行 IO 时首先需要进行寻址而寻址的过程如下 先定位在哪一个磁道(Track)/柱面(Cylinder)此时磁头不用动了再定位磁头Head定位盘面最后定位在哪一个扇区Sector。 可见磁盘中定位任何一个扇区采用的硬件基本定位方式先柱面、再磁头、最后扇区即CHS定位法。 能定位任意一个肯定也能定位任意多个定位方式相同。 拓展知识 在大型公司中一个磁盘通常是几个T里面保存着大量的用户数据为了用户数据安全企业需要要对不用/损坏的磁盘进行消磁 常见的磁盘消磁方法有两种一、加热其缺点是销毁成本高销毁的磁盘不能回收造成浪费并且不能保证所有盘的数据全部消磁。二、向磁盘中写入垃圾数据或将磁盘格式化其缺点是某些磁盘厂商有能力恢复之前的数据。 所以一般大公司都会和磁盘厂商进行协商要求厂商提供磁盘深度清理的定制协议功能比如企业通过向磁盘输入协议密文来对磁盘进行深度清理。 注一般大型公司都有自己固定的磁盘供应厂商该企业的大部分磁盘都由该厂商提供但是企业选择磁盘是不会全部都选一家公司的盘的一是防止杀熟二是防止磁盘批量化故障。 1.3 磁盘的逻辑结构
理解逻辑结构
我们以磁带的结构来引出磁盘的逻辑结构如图磁带盒里面一共有两个齿轮其中一个齿轮上面缠绕着一圈圈的磁带当我们把磁带盒插入磁带录音机后磁带里面的音频数据就会读取然后通过录音机播放出来当我们把磁带盒拆开后我们可以发现磁带扯出来后其结构是线性的也就是说磁带里面的数据是按线性方式来读取的。 如果一摞磁盘有两片即四个面内存为500GB根据线性的方式展开理解可以看成如下结构
通过这样的展开我们可以把这样的逻辑结构看成一个arr[n]数组数组中每一个元素的大小都为512byte。此时如果我们对磁盘进行管理那就可以直接对这个数组进行管理。这就是先描述再组织。
那此时如果想要找到指定的扇区只要知道这个扇区的下标就可以定位磁盘指定的扇区。在操作系统内部我们称这种地址为LBA地址(Logical Block Address)即逻辑块地址。 找到了LBA地址我们需要确定CHS地址。LBA 地址转 CHS 定位例子 假设一个磁盘有两个盘片每个盘片有两个盘面每个盘面有10个磁道每个磁道有100个扇区现在某个扇区的LBA地址为1234求该扇区在磁盘上的具体位置 最后为什么要将CHS进行抽象呢直接CHS不行吗有如下两个原因
便于管理。不想让OS的代码和硬件强耦合。硬件变化时不会影响操作系统
二.理解文件系统
2.1 对IO单位的优化 虽然对应的磁盘的访问的基本单位是512字节但是依旧很小从而导致磁盘的频繁IO。因此OS内的文件系统定制了进行进行多个扇区的读取-1KB/2KB/4KB(常见)为基本单位从而减少IO的过程哪怕只读取或者修改1bit也必须将4KB加载到内存进行读取或修改如果必要再写回磁盘。 注文件系统以 4KB 作为数据 IO 的单位那么当读取的数据小于 4KB 时我们仍然需要读取 4KB 数据那么就有同学可能担心数据无用的问题其实计算机中的局部性原理已经很好的解决了这个问题。实际上这是一个空间换时间的过程。
2.2 磁盘分区与分组
对于一个GB级别比如500GB的磁盘文件系统IO单位为4KB显然不够大即便之前已经分区也不便于管理因此还需要继续分组。 对于每次的分组来说如果能将一组管理好那么其他组用同样的方式也能管理好因此采用分治的思想最终只要能将这块5GB的磁盘空间管理好那么整个磁盘都可以管理好。以上述方式分组之后产生的需要管理的部分就变成了如下的结构
每个分区的第一部分数据是 Boot Block 启动块后面才是各个分组它与计算机开机相关我们不用关心。
现在我们只需要管理好一个分组然后管理模式复制到其他分组就可以管理好一个分区再将一个分区的管理模式复制到其他分区就可以管理好整个磁盘了。其中操作系统对一个分区的管理就被称为文件系统。
2.3 分组的管理方法 超级块Super Block
存放文件系统本身的结构的全部信息。记录的信息主要有block 和 inode的总量未使用的block和inode的数量一个block和inode的大小最近一次挂载的时间最近一次写入数据的时间最近一次检验磁盘的时间等其他文件系统的相关信息。Super Block的信息被破坏可以说整个文件系统结构就被破坏了。Super Block不放在分区的位置是为了能够进行备份一旦其他Super Bloack出现异常就可以将其他正常的SuperBlock拷贝过来。
Inode
文件 内容属性。 Linux的文件属性和内容是分开存储的文件属性通过Inode存储Inode是固定大小的一个文件一个Inode。需要注意的是文件的名字并不在Inode中存储。文件内容采用data block进行存储它的大小随着应用类型的变化而不断变化。
Inode为了区分彼此每一个Inode都有一个自己的ID通过ls 的-i选项即可查看
Inode Table
Inode Table保存了分组内部所有可用的已经使用没有使用Inode。Inode Table在划分组的时候大小就已经确定好了。创建文件时就需要在Inode Table中找到没有被使用过的Inode把文件中的所有属性填充到这个Inode里面这样文件的属性就都有了。
Data blocks
数据块保存了分组内部所有文件的 data block 块数据块的大小不是固定的它随着应用类型的变化而变化其中包含了一个文件的全部或者部分内容 (文件内容太多时需要使用多个 data block)。
Inode Bitmap
Inode对应的位图结构。无论是Inode Table还是Data blocks在创建文件时都必须有一个查找功能。对于Inode Table来说Inode Bitmap通过比特位的方式如果有100个文件那就有100个比特位位图中的比特位的位置和当前文件对应的inode的位置是一一对应的如果是0就代表没被占用可以使用1就代表已被占用不能使用。
Block Bitmap
数据块对应的位图结构。位图中的比特位位置和当前data block对应的数据块位置是一一对应的关系。哪一个块被占用就由0置1哪一个块被释放就由1置0。
GDT (Group Descriptor Table)
块组描述符描述块组属性信息。对应分组的宏观属性信息比如一共有多少个Inode一共有多少数据块Inode备用了多少数据块备用了多少等等。虽然这些数量可以计算但是计算会消耗性能所以GDT存在的意义就是存储这些数量以免计算。
2.4 文件操作 大部分上述已经谈到过在这里总结一下。 新建文件
在了解了一个分组的具体组成之后我们就知道新建文件时的对应操作了 在 Inode bitmap 里面查找为0的比特位编号置为1然后将文件的所有属性填写到 inode table 对应编号下标的空间中再在 block bitmap 中查找一个/多个为0的比特位编号将其置为1然后将文件的所有内容填写到 data blocks 对应编号下标的空间中最后再修改 super block、GDT 等位置中的数据。同时需要将新文件文件名与 inode 的映射关系写入到目录的 data block 中。
获取文件 inode
在 Linux 中查找文件统一使用 inode 编号但是我们平时只适用过文件名从没有使用过 inode那么操作系统是如何将文件名与 inode 一一对应的呢答案是通过目录。目录和普通文件不同目录的内容是下级目录或者普通文件所以目录的 data block 里面存储的是当前目录下文件名与 inode 的映射关系。
所以当我们在某一个目录下使用文件名查找文件时操作系统会读取目录 data block 里面的数据找到文件名对应的 inode 编号找不到就提示 文件不存在。而当我们在目录下新建文件/文件夹时操作系统会向目录 data block 里面写入新文件与 inode 的映射关系。这也是为什么在目录下读取文件信息需要 r 权限在目录下新建文件需要 w 权限的原因。
读取文件属性
先通过目录 data block 得到文件的 inode 编号然后在 inode bitmap 查看对于编号比特位是否为1检查 inode 有效性然后从 inode table 中读取对应 inode 中的文件属性。
注inode 编号可以跨分组但不可以跨分区即同一分区内 inode 是统一编号的。
读取文件内容
对文件的保存就是对内容属性的保存内容保存在数据块里属性保存在Inode里。
当我们查找一个文件的时候统一通过Inode编号查找。查找文件的内容时其存储内容的所有数据块的地址都在Inode中的一个数组中存放通过数组中每一个元素存储的值就可以指定找到这个文件的全部内容。当然通过Inode数组中的元素找到的数据块这个数据块存放的也有可能是其他数据块的地址。
struct inode {int id;mode_t mode;int uid;int gid;int size;//...int blocks[15];//存放数据块地址的数组
};注一般来说blocks 里面前12个元素存放的都是一个 block 编号代表 data blocks 里面的一块空间但是最后三个元素不同虽然它们存放的也是一个 block 编号但 data blocks 对应 block 编号中存放的内容却很特殊blocks[12] 指向的 data block 中存放的是一级索引即其中存放的也是一个类似于 blocks[15] 的数组指向多个 data blockblocks[13] 指向的 data block 中存放的是二级索引即其中存放的内容类似于 blocks[12]以此类推blocks[14] 里面存放的是三级索引。这样即使该文件很大操作系统也能够成功读取文件的内容。
删除文件
只需要将 inode bitmap 和 block bitmap 里面对应比特位置为 0 即可后面新文件的文件属性和文件内容直接覆盖原来已删除文件的属性和内容。
恢复文件
在理解了删除文件的原理之后我们就明白文件删除之后是可以恢复的 – 操作系统包含了文件的日志信息会将文件名与 inode 的映射关系保存在一个临时的日志文件里我们通过这个日志文件找到误删文件的 inode然后将 inode bitmap 里面对应的比特位重新置为1再通过 inode 结构体中的 blocks 成员找到所有的数据块再将 block bitmap 中对应比特位置为1即可
不过这一切的前提是原文件的 inode 没有被新文件使用 – 如果新文件使用了原文件的 inode那么对应的 inode table 以及 data block 里面的数据都会被覆盖所以文件误删之后最好的做法就是什么都别做避免新建文件将原文件的 inode 占用。
三.软硬链接
首先touch 一个myfile.txt
3.1理解硬链接
创建硬链接的指令hard_file.link就是myfile.txt的硬链接 ln myfile.txt hard_file.link 创建myfile.txt的硬链接之后各种属性与目标文件都相同主要的是Inode相同。因此实际上这两个都是同一个文件那为什么一个文件有两个名字实际上在之前的分组管理方法中我们知道InodeInode作为文件的属性其中包含着对应文件的数据块的地址而给这个文件硬链接后实际上就多了一个映射关系不仅可以从myfile.txt找到对应的文件内容通过hard_file.link同样可以找到与此同时存在一个计数器的变量也会从1变成2引用计数也称硬链接数。硬链接数默认是1的原因每一个普通文件本身就有一个文件名和一个Inode即一个映射关系。
建立硬链接做了什么 通过现象可以看出-随着不断输入硬链接与目标文件大小同时发生变化因为都储存到一个位置。因此也就解释了创建一个硬链接根本没有新增文件因为没有给硬链接分配独立的Inode。既然没有创建文件硬链接本身也就没有自己的属性集合用的也是目标文件的Inode和内容。 如果将myfile.txt删掉通过硬链接仍然可以访问到该文件的内容因为只是去掉了一个映射关系计数器减1还存在一个映射关系可以访问。因此可以看出只有当硬链接计数器为0时才算是将一个文件真正的删掉。 注意这样操作与重命名的意义是不同的。 3.2 理解软链接
创建硬链接的指令soft_file.link就是myfile.txt的软链接 ln -s myfile.txt soft_file.link 软链接和硬链接并不一样不采用计数器的方式因为软链接的文件与目标文件是两个文件软链接虽然同样可以找到链接对应的文件但一旦将myfile.txt删掉软链接也会失效这是因为软链接存的并不是文件的Inode而是目标文件的路径。
所以软硬链接的区别实际上就是与目标文件的Inode是否相同
软链接的用法
我们将其他目录下的程序以软链接的方式能够更加方便的找到并运行起来。 可以看出软链接就相当于Windows的快捷方式。注删链接文件rm和unlink都可以。 3.3 理解. 我们发现empty作为目录它的硬链接数量初始值为2这是有原因的说明仍有一个文件与empty的Inode相同构成硬链接。
通过查看我们发现隐含的.实际上就是empty的硬链接且.文件是自动生成的所以目录初始的硬链接为2的原因就是因为.的硬链接。
所以在empty目录中cd .就是cd empty
3.4 理解…
如果在empty目录中有新建了一个目录dir我们发现empty的硬链接数又发生了变化即从2变成了3。 那么说明一定又多了一个硬链接的文件
可以发现dir中的..的Inode与empty的Inode相同可见从文件系统的层面再次加深了对cd .. 的理解cd ..就是cd empty。 如果想给目录主动建立硬链接呢
我们发现这是不被允许的那Linux为什么不允许普通用户给目录硬链接呢 如果目录要创建硬链接的话那么它的子目录以及子文件都要创建相应的硬链接这无疑带来了很大的局限 如果一个目录下有着很多的内容那么一个硬链接的创建将会是无法想象的可以说对应目录下的每个内容都要与源目录下的每个内容创建相应的硬链接。.和..的硬链接是OS自己建立的。 当然目录可以建立软链接因为只是一个快捷方式。
四.动态库和静态库 注在Linux第六篇gcc/g及Makefile中提到过。 4.1 库的理解
对于我们所编写的文件
有三类
主函数main.c功能函数my_add.c、my_sub.c头文件声明my_add.h、my_sub.h
我们需要将功能函数通过gcc -c的方式形成二进制文件my_add.o、my_sub.o再将.h和.o的文件打包放在一起不能将主函数放在包内而打包后形成的.o文件就被称为库。
4.2 什么是静态库
一、定义
静态库是将库中的代码拿到本地即当程序运行的时候就不再需要链接静态库。即可以 ”离线访问“
静态库一般是libXXX.a格式XXX就对应文件名。因此对于上述文件可以通过编写makefile的方式来生成静态库/打包成libmymath.a。
命令ar -rc libmymath.a add.o sub.o或者ar -rc $ $^
统一打包成libmymath.a之后就可以看到这个文件 这就是打包生成的静态库。
二、Linux能够编写C语言的原因
那为什么我们能够在Linux中编写C语言实际上也是同样的道理我们发现Linux中同样存在着编写的库和相应的头文件正是通过这些库我们在C语言编写代码时通过#include从c库获取信息才能够写出有效的代码。
三、静态库和静态链接
因此在自己写静态库交付给别人的时候我们同样需要将库(.a .so)和.h文件交付给用户才能执行库中的功能。将库和.h文件打包在一起发布output才能供给用户使用因此这种操作我们应该在makefile文件中提前设置好 生成压缩包之后用户就可以下载这个压缩包从而使用我们所编写的库。 实际上下载就是所谓的拷贝即我们可以将这个压缩包copy到另一个路径和新的main.c进行链接通过tar -xzf mylib.tgz解压之后就可以将我们所写的库引入到新的位置去链接了此时利用gcc指令需要注意的是后面必须跟上我们自己定义的静态库和.h文件的路径并且必须跟上相应静态库的名字注意名字是去掉前缀和后缀。 这个时候问题来了为什么平时我们写代码的时候不需要跟上静态库的位置呢事实上对于gcc/g来说其默认已经跟上了对应的标准库但由于现在的库使我们自定义的因此gcc不会找到所以我们需要主动添加上。 这样生成的mymath就可以直接执行完成静态链接。
注意 选项后面的空格带不带都是可行的。 形成一个可执行程序可能不仅仅依赖一个库 gcc默认是动态链接的建议行为。对于具体的一个库究竟是动态库还是静态库取决于你提供的是动态库还是静态库。
四、安装库
除了上面通过选项的方式找到对应的头文件及相应的静态库还有一种方式可以不用这些选项-通过安装的方式即把我们自定义的头文件和库拷贝到默认路径下
这样通过如下方式同样可以执行 不过·目前不太推荐安装的方法因为正处于现学的过程写的东西一般没什么用。 最后别忘了删除刚才实验的文件这个过程实际上就叫做卸载
4.3 什么是动态库
一、定义
程序在运行的时候才去链接动态库的代码多个程序共享使用库的代码。即必须 ”在线访问“
静态库一般是libXXX.so格式XXX就对应文件名。拿my_sub.c举例与静态库生成.o文件不同的是动态库的.o文件需要gcc -c -fPIC my_sub.c即多了一个选项-fPIC【产生位置无关码(position independent code)】
想要将全部的.o文件打包也与静态库有所不同同样多了一个选项shared【表示生成共享库格式】即gcc -shared -o libmymath.so my_sub.o my_add.o 可以看出静态库通过ar形成动态库通过shared形成。 二、动态库与动态链接
接下来与上面一样将动态库和.h文件打包这次采用手动的形式
拷贝到test中可见唯一不同的就是动态库后缀变成了.so
接下来仍然和静态库的执行一样通过gcc 链接生成可执行文件
但是出现了问题找不到对应的文件这与静态库就出现了差别静态库这里明明可以直接运行与静态库一样在gcc的时候告诉了指定的头文件库文件。
解决方式
但是我们告诉的对象是gcc实际上当编译完成就与gcc无关了。但对于动态库来说其链接不仅仅在编译时需要在运行时同样需要因为动态库是在运行时才慢慢加载动态库的因此在运行时OS和Shell也需要知道库在哪里。所以还差一步告诉OS和shell动态库在哪里OS和shell一般加载库的时候都在lib64系统目录下加载而你的库不在系统目录下因此解决办法 将我们自己写的库拷贝到系统路径下方便OS和shell找到。但上面的方法谁都能做还有其他方式能够找到动态库。
方案二环境变量的方式
将动态库添加到环境变量中方便OS和Shell找到。
此时就能够找到对应的动态库
最后也可以运行成功了 但添加环境变量会存在一个问题当我们把窗口关闭后即再次登录时我们自定义的环境变量就不复存在因此再次运行,mymath同样会找不到对应的动态库这种方式适合平时的测试。如果想一直保存就需要配置系统的环境变量的文件非常麻烦。因此如果想永久保存还有两种其他方案 方案三配置文件的方式 首先找到系统中的ld.so.conf.d路径在其中任意创建个文件但是格式必须为.conf。其次将动态库的路径写到这个文件中。最后通过ldconfig更新路径缓存。
通过这样的方式OS和Shell就会找到动态库并永久有效。 方案四当前路径创建软链接的方式 这样同样可以。 方案五系统路径lib64路径创建软链接的方式 这样同样可以并能够永久保存。方案四和方案五实际上是同一种做法。 由于在实际操作中可能不止一个库因此就有这么三种情况 全是静态库只能静态链接全是动态库只能动态链接动静结合取决于gcc但只要有一个动态库就是动态链接。 4.4 demo
对于第三方库我们也可以下载这样的库。举个例子可以下载一个图形化的界面库即ncurses库
普通用户安装方式sudo yum install -y ncurses-devel root用户就去掉sudo 通过这个库就可以通过引入这个库的头文件通过代码的实现从而执行一个简单的图形化界面。
代码实现 通过命令gcc main.c -lncurses执行生成a.out文件 执行a.out./a.out 按q退出。 五.动静态库的加载理解 回顾进程地址空间部分知识 我们曾经讲过程序不仅在运行时作为进程地址空间存在在编译的时候就已经以虚拟地址的方式把我们的程序编译好即程序在没有加载到内存的时候就已经有了代码区、全局数据区已初始化未初始化只读……堆区栈区没有 5.1 加载静态库
对于静态库来讲静态库不需要加载而程序需要加载。当静态库链接的时候实际上是将代码printf拷贝进程序中所以后面程序运行的时候就不再依赖于静态库。
而一旦有很多程序静态库就会拷贝大量重复的代码分给不同的程序。通过进程地址空间的知识我们知道当静态库拷贝代码给程序时实际上是把代码拷贝进了代码区。因此在程序运行形成进程地址空间时静态库中的代码只能被映射到进程地址空间相应的代码区中未来的这段代码必须通过相对确定的地址位置进行访问。 5.2 加载动态库
具体分成以下步骤 对于动态链接来说可执行程序中存放的是动态库中某具体 .o 文件的地址同时由于组成动态库的可重定向文件是通过位置无关码 fPIC 生成的所以这个地址并不是 .o 文件的真正地址而是该 .o 文件在动态库中的偏移量与C中的虚函数表一样 然后就是程序运行的过程操作系统会将磁盘中的可执行程序加载到物理内存中然后创建 mm_struct建立页表映射然后开始执行代码当执行到库函数时操作系统发现该函数链接的是一个动态库的地址且该地址是一个外部地址操作系统就会暂停程序的运行开始加载动态库 加载动态库操作系统会将磁盘中动态库加载到物理内存中然后通过页表将其映射到该进程的地址空间的共享区中然后立即确定该动态库在地址空间中的地址即动态库的起始地址然后继续执行代码 此时操作系统就可以根据库函数中存放的地址即 .o 文件在动态库中的偏移量再加上动态库的起始地址得到 .o 文件的地址然后跳转到共享区中执行函数执行完毕后跳转回来继续执行代码段后面的代码。这就是完整的动态库的加载过程。 因此可以看出动态库并不像静态库一样不需要拷贝大量的相同代码多个程序可以共享一份动态库中的代码。