文章目录
- 磁盘结构
- 磁盘存储结构
- 磁盘的逻辑结构
- 引入文件系统
- 理解文件系统
- inode 映射 data blocks
- 文件名与inode的关系
- dentry树
- 文件描述符与进程之间的关系
- 深刻理解软硬链接
- 软链接
- 硬链接
- 动静态库
- 静态库
- 1. 手动制作静态库
- 2.调用静态库
- (1)安装到系统
- (2)自己指定查找路径
- (3)自己创建include和lib,发布给别人
- 动态库重复上述操作
- (1)安装到系统
- (2)自己指定查找路径
- (3)自己创建include和lib,发布给别人
- 解决方法 (环境centos7)
- 注意事项
- 动态库的理解,动态库加载和进程地址空间
在文件1当中我们主要讲解了什么是文件描述符?同时提出了文件内核缓冲区以及用户级缓冲区的概念,理解了什么是重定向。
以上的都是对于已经打开了的文件,他们是存在内存当中的。可以一个磁盘可能有10000个文件,已经打开了100个,那么剩余的9900个文件呢?-》那肯定就是在磁盘当中喽!那么我们想要打开一个在磁盘当中的文件首先这个磁盘当中得有这个文件,其次我们需要知道这个文件的地址。可是每一个文件都有一个地址,那么磁盘就一定要对这些文件进行管理。
举个例子🌰:
在生活当中,我们从网上买了一个东西,可能并不是快递一到了我们就去拿了,一般都会放在菜鸟驿站当中,那么菜鸟驿站肯定不会说直接把这些快递堆一块,我们来拿快递的时候光找都要找1~2小时,而是把每一个快递都管理起来,比如每一个快递都有一个编号: 17 − 1024 17-1024 17−1024代表着17号柜子1024号。这个编号对应其中一个快递,并且是唯一的。这就相当于磁盘当中文件的地址。对于每一个快递都需要这么管理起来,同时如果哪个柜子太满,另一个柜子又太空闲,那么对于驿站管理员来说就需要对快递进行管理等等操作,我们把这样的行为称为快递系统,同时在磁盘当中也需要一个文件系统来管理没有被打开的文件。
那么我们就需要解决:
- 磁盘是如何管理这些文件的呢?
- 如何理解文件地址呢? 这里我们知道文件地址可以定位文件 这里我们知道文件地址可以定位文件 这里我们知道文件地址可以定位文件
磁盘结构
上图是我们台式电脑中机械硬盘拆开了之后的样子,这里我们主要谈两个,磁盘和磁头,我们磁盘再不断地顺时针旋转,然后磁头可以左右移动来读取磁盘当中的数据(俯视图)。
磁盘存储结构
磁盘是由盘片构成的,每一个盘片有两面或者称为表面,表面覆盖着磁性记录材料。磁盘中央有一个可以旋转的主轴,他让盘片可以旋转起来,每个磁盘通常包含一个或者多个盘片。
如上图,其中我们一圈一圈的同心圆称为磁道,每一个磁道划分为相同个数的扇区(只不过内层的间隙小,外层的间隙大并且每个面的扇区数是由最内层的扇区数决定的,每一个扇区之间有间隙隔开。每个扇区中可以存放 512 512 512字节的数据 ( 扇区是磁盘当中最基本的存储单元 ) (扇区是磁盘当中最基本的存储单元) (扇区是磁盘当中最基本的存储单元)。
如上图,这里假设我们一共3片盘片,每一个盘片都会有两个磁头,就是说每一片盘片都有两个面,并且每一个面都会有一个磁头。
那么磁盘是如何读取数据的呢?
当我们盘片在旋转的过程中,移动到对应的磁道中,通过旋转再定位到一个一个的扇区当中。 这里可以把盘面上一粒微小的灰尘都看做一块巨石,如果磁头碰到了这样的一块巨石就会停下来,撞到盘面 − − 所谓的读 / 写 这里可以把盘面上一粒微小的灰尘都看做一块巨石,如果磁头碰到了这样的一块巨石就会停下来,撞到盘面--所谓的读/写 这里可以把盘面上一粒微小的灰尘都看做一块巨石,如果磁头碰到了这样的一块巨石就会停下来,撞到盘面−−所谓的读/写
-
所以旋转的本质是为了确定是哪个扇区。
-
而磁头臂在半径中横向移动是为了确定在哪一个磁道。
同时我们每个面的每个磁头移动的时候都是一起移动的,这里我们管每个面的每个磁头共同形成的那个面称为柱面 ( c y l i n d e r ) (cylinder) (cylinder)。如下图:
并且我们磁盘中柱面有6个磁头所在的磁道构成,从外向内使用,依次编号为 [ 0 , 1 , 2 , 3... ] [0,1,2,3...] [0,1,2,3...],盘面也是从上往下依次编号为 [ 0 , 1 , 2 , . . . ] [0,1,2,...] [0,1,2,...]。
如何定位一个扇区呢?
当我们需要定位一个扇区的时候我们需要定位到底在哪一个柱面(cylinder) 上,其次需要定位在哪一个 磁头(header) 上,最后才是定位到哪一个扇区(sector) 当中。所以在磁盘当中我们管这个叫做CHS
寻址。
我们在linux中可以使用fdisk -l /dev/vda
来查看我们的磁盘情况:
如果是虚拟机的话则是/dev/sda
。如上图,可以说明确实是存在扇区这个概念的。
磁盘的逻辑结构
如上图,我们可以把每一个磁道都拉直,同时扇区的下标是从1开始的,这样每一个扇区都有一个数组下标位置了,这个位置下标称为LBA
地址,但是在磁盘当中真是的物理结构还是我们上述讨论的结构,所以磁盘当中还是会通过CHS
去寻址,但是我们操作系统却可以使用LBA地址去转化CHS地址。
上述我们说过,一个磁盘可以有多个盘片,每一个盘片又有俩个磁头,这里假设我们有三个盘片那就是6个磁头,每一个磁头都是共同移动的。6个磁头指向6个磁道,这些相同半径磁道构成一个柱面,所以我们磁盘可以看做是一个个柱面卷起来的圆柱体。并且每一个磁道中的扇区个数是相同的,那么把这个圆柱体剪开就相当于把每个磁道的扇区展开,这不就是一个二维数组吗:
那么多个柱面合在一起不就是一个三维数组吗,同时无论是二维数组还是三维数组,我们都可以使用一个一维数组来表示:
如上图,所以以这样的逻辑结构我们就可以使用数组下标位置来表示扇区位置 L B A LBA LBA(logical block address)地址。那么LBA地址是如何与CHS地址进行相互转换的呢?
C H S = > L B A CHS\ =>\ LBA CHS => LBA: 柱面号 ∗ 柱面扇区总数 ( 磁头数 ∗ 磁道扇区总数 ) + 磁道号 ∗ 单个磁道扇区总数 + 扇区号 − 1 柱面号*柱面扇区总数(磁头数*磁道扇区总数)\ +\ 磁道号*单个磁道扇区总数\ + 扇区号-1 柱面号∗柱面扇区总数(磁头数∗磁道扇区总数) + 磁道号∗单个磁道扇区总数 +扇区号−1
同时以上式子有以下关系:
柱面扇区总数 > 单个磁道扇区总数 > 扇区号 − 1 柱面扇区总数 > 单个磁道扇区总数 > 扇区号-1 柱面扇区总数>单个磁道扇区总数>扇区号−1
L B A = > C H S LBA\ =>\ CHS LBA => CHS:根据上述关系可用LBA反推CHS
- 柱面号 = L B A / / 柱面扇区总数 ( 磁头数 ∗ 磁道扇区总数 ) LBA\ //\ 柱面扇区总数(磁头数*磁道扇区总数) LBA // 柱面扇区总数(磁头数∗磁道扇区总数) (//表示整除,向下取整)
- 磁道号 = ( L A B % 柱面扇区总数 ) / / 磁道扇区总数 (LAB\ \%\ 柱面扇区总数)\ //\ 磁道扇区总数 (LAB % 柱面扇区总数) // 磁道扇区总数
- 扇区号 = ( L A B % 磁道扇区总数 ) + 1 (LAB\ \%\ 磁道扇区总数)+1 (LAB % 磁道扇区总数)+1 (LBA的下标是从1开始的)
所以从现在开始我们操作系统定位扇区是需要使用LBA地址即可。但是实际当中一个扇区大小为 512 512 512字节,还是太小了,所以一般操作系统访问磁盘都是以 4 K B 4KB 4KB大小进行访问,也就是 8 个扇区 8个扇区 8个扇区,我们管着8个扇区叫做一个块。
如上图,我们知道了块号也可以知道LBA地址,知道了LBA地址也就可以转化为CHS地址,这样操作系统就可以定义磁盘当中任意一个扇区了,能找到一个扇区同时也可以找到多个扇区,以这种方式我们操作系统就可以管理文件了。
引入文件系统
所以我们磁盘可以看做是一个一个的块组成的,知道了磁盘的总大小我们就知道了块的个数 磁盘总容量 / 4 K B 磁盘总容量/4KB 磁盘总容量/4KB,那磁盘是一个一个去管理这个4kb大小的块吗?有些磁盘的空间可以会有几TB,或者几十TB,这样的话管理的成本太高,所以一般磁盘管理是对磁盘进行分区,同时每个分区还要进行分组,只要把每一个组管理好了,就相当于把分区管理好了,每一个分区管理好了那么整个磁盘也就管理好了。
举个生活中的例子🌰:就像我们国家960万平方千米的国土,分为了一个又一个的省,每个省又分为了一个有一个的市,把每一个市管理好了,就相当于把省管理好了,把每一个省管理好了就可以把整个国家管理好。
如上图,在linux中我们/dev代表的就是磁盘,而vda1代表的就是当前磁盘的分区。
对于不同磁盘分区可以使用不同的文件系统,这样即使一个分区挂掉了其他分区也不受影响。我们主要来讲解ext系列中的ext2文件系统。
如上图所示,对于每一个分组而言有一下几种结构:
- Block Group:ext2文件系统会根据分区的大小划分为数个Block Group。而每个Block Group都有着相
同的结构组成。政府管理各区的例子- 超级块(Super Block):存放文件系统本身的结构信息。记录的信息主要有:bolck 和 inode的总量, 未使用的block和inode的数量,一个block和inode的大小,最近一次挂载的时间,最近一次写入数据的时间,最近一次检验磁盘的时间等其他文件系统的相关信息。Super Block的信息被破坏,可以说整个文件系统结构就被破坏了
- GDT,Group Descriptor Table:块组描述符,描述块组属性信息。
块位图(Block Bitmap):Block Bitmap中记录着Data Block中哪个数据块已经被占用,哪个数据块没
有被占用- inode位图(inode Bitmap):每个bit表示一个inode是否空闲可用。
- block位图(blockBitmap):每个bit表示一个block是否空闲可用。
- inode节点表:存放文件属性 如 文件大小,所有者,最近修改时间等
- 数据区:存放文件内容
如上图,文件 = 内容 + 属性,其中属性是存储在 i n o d e inode inode结构体中,我们只来讨论大小为 128 128 128字节的情况。
分组当中每一个文件都会有一个 i n o d e inode inode结构体里面会有一个 i 结点编号 i结点编号 i结点编号,他们存储在 i n o d e t a b l e inode\ table inode table中,而一个文件对应的内容则是存储在 D a t a b l o c k s Data\ blocks Data blocks中。同时我们的 i n o d e b i t m a p inode\ bitmap inode bitmap就是一个位图,他表示的是我们 i n o d e t a b l e inode\ table inode table中的某一个 i n o d e 号 inode号 inode号是否被使用,在 l i n u x linux linux中我们可以使用 l s − i ls\ -i ls −i来显示 i n o d e inode inode号。
i n o d e inode inode结构体中还会存在 b l o c k n u m [ ] block\ num[] block num[]数组,它里面存储着一个文件对应的数据块号,数据块存储在 d a t a b l o c k s data\ blocks data blocks中,每个大小都为 4 k b 4kb 4kb。
而数据块也有一个 b l o c k b i t m a p block\ bitmap block bitmap表示这数据块是否被使用。
G r o u p D e s c r i p t o r T a b l e Group\ Descriptor\ Table Group Descriptor Table:里面存储着是当前分组的 i n o d e t a b l e inode\ table inode table和 d a t a b l o c k s data\ blocks data blocks的使用情况(已使用多少个,还有多少个空闲)。
S u p e r b l o c k Super\ block Super block:超级块,表示文件系统。它存储则是整个分区中的 i n o d e inode inode和 b l o c k block block个数,一般一个分区当中不会每一个分组都会有一个 s u p e r b l o c k super\ block super block,当一个 s u p e r b l o c k super\ block super block出问题之后,整个文件系统就崩了,这时候只需要拷贝其他的 s u p e r b l o c k super\ block super block信息即可。
理解文件系统
对于每一个分区而言我们都是一整套的 i n o d e inode inode,也就是说我们不同分区当中可以会有同一个 i n o d e = = 100 inode==100 inode==100的文件,各个分区相互独立。那么文件系统是如何分配 i n o d e inode inode的呢?
如上图,对于每一个分组而言我们只需要设置每个分组的起始 i n o d e inode inode即可,对于 d a t a b l o c k data\ block data block也是如此。
- 如何删除一个文件?
我们拿着对应的 i n o d e inode inode号,求跟起始 i n o d e inode inode比较找到对应的分组,然后减去起始 i n o d e inode inode去分组当中的bitmap中把 1 − > 0 1\ ->\ 0 1 −> 0即可,所以对于删除一个文件我们只是把对应的bit位由1变为0,所以删除了一个文件我们其实是可以恢复的,但这只是在当前位置的 i n o d e inode inode号没有被再次使用的情况。
- 如何新增一个文件
去每一个组当中的 G r o u p D e s c r i p t o r T a b l e Group\ Descriptor\ Table Group Descriptor Table看 i n o d e b i t m a p inode\ bitmap inode bitmap是否已满,如果没有满在遍历找出bit位为0,再把它置为1,然后返回当前位置 + s t a r t i n o d e start\ inode start inode。
- 如何修改一个文件?
根据 i n o d e inode inode号找到一个文件的 i n o d e t a b l e inode\ table inode table去查看对应的 b l o c k n u m [ ] block\ num[] block num[]然后把对应的数据块 ( 4 k b 大小 ) (4kb大小) (4kb大小)拷贝到磁盘当中,修改完之后再拷贝回去。这里还需要注意我们的 d a t a b l o c k s data\ blocks data blocks其实可以分组存储的。
inode 映射 data blocks
对于一个分组而言就是一开始所有的位置所需要的空间已经划分好了, S u p e r b l o c k Super\ block Super block把每一个分区中的 I n o d e Inode Inode和 b l o c k block block数量已经给定了,那么每一个分组的 I n o d e Inode Inode和 b l o c k block block数量就也给定了,而且对于一个分组 ( 10 G B 为例 ) (10GB为例) (10GB为例)而言,其中除了 d a t a b l o c k s data\ blocks data blocks之外其他的结构可能也就占据 100 M B 100MB 100MB,其余的 9.9 G B 9.9GB 9.9GB都是有数据块占据的,那么一个 i n o d e inode inode也就 128 128 128字节,如果用一个整数来映射数据块号的话也就只能映射几十个。所以 i n o d e inode inode中采取多级指针的方式来指向数据块的,如下图:
如上图,我们 i n o d e inode inode有12个直接指向数据块,其他的一级间接块索也是指向一个 4 k b 4kb 4kb大小的数据块,只不过里面不存数据,而是存的其他数据块的标号,类似一个一维数组,那么一个编号假设需要4个字节,那一个块就可以存储 1024 1024 1024的数据块号,这1024个数据块可以指向的数据块总大小为 1024 ∗ 4 k b = = 4 M B 1024*4kb == 4MB 1024∗4kb==4MB,同理我们二级索引表指向的数据块不存数据,存的是一级索引表的编号,那么可以指向的数据块总大小为 1024 ∗ 一级索引表大小 = = 4 G B 1024*一级索引表大小 == 4GB 1024∗一级索引表大小==4GB,三级索引就是40GB。
文件名与inode的关系
我们上述增删查改文件好像都是与inode有关,可以平常我们在linux中都是用的文件名,并且我们的文件名不存在$inode $结构体当中,那么我们是如何通过文件名找到文件的呢?
我们的目录也是文件,是文件就 = 内容 + 属性。
所以我们的目录存储的是文件名与 i n o d e inode inode的映射关系(互为映射),当我们想要查找文件的时候就需要去目录中查找对应文件名对应的 i n o d e inode inode号。所以现在我们就可以在来重新理解目录的 r w x rwx rwx权限,没有 r r r权限你就不能访问目录当中文件名与 i n o d e inode inode的映射关系,没有 w w w权限,就不能向目录文件中的数据块中写入,没有 x x x权限本质就是打不开文件。而且对于上述讲的文件系统而言,并不区分到底是文件还是目录,本质都是 i n o d e inode inode号。
那么为什么 l i n u x linux linux要使用这种方式呢?效率,对于我们查询字符串的时候,比较的时间为 O ( n ) O(n) O(n),而数字的比较为 O ( 1 ) O(1) O(1)。
如上图,包括我们的拥有者和所属组也是整数来存储的,这也是为什么我们会有 u i d uid uid和 g i d gid gid的原因。
dentry树
现在我们知道了找到一个文件需要去目录当中去查找文件名对应的 i n o d e inode inode号,可是目录也是文件啊,目录的 i n o d e inode inode号谁来提供呢?
如上图,我们查找一个文件,需要解析目录找到对应的 i n o d e inode inode,上图我画的是依次往上解析,实际是从根目录往下解析的。而根目录的 i n o d e inode inode是直接给定的。
所以为什么每一个文件都要有路径呢?就是因为要通过路径层层外下递归去找到 文件,那么路径是谁给的呢?-> 进程,所以每一个进程都需要有一个cwd。
如上图,我们每一次解析路径都是访问磁盘的文件系统,这样效率太慢了,所以linux中就有一种内存结构 s t r u c t d e n t r y struct\ dentry struct dentry来对所访问的目录或者文件进行缓存,根目录则是本来就有的。所以当我们想要查询一个已经查询过的文件时,不要再按个解析路径了,只需要遍历这个dentry树即可。
所以为什么find 路径 -name 查询文件的时候第一次比较慢,之后就很快了呢?就是因为路径被缓存了。
文件描述符与进程之间的关系
如上图,对于打开一个文件,我们会先构建一个 d e n t r y dentry dentry结点,把结点挂在对应的目录底下,最后再创建一个 s t r u c t f i l e struct\ file struct file返回对应的 f d fd fd。
同时文件的内容拷贝到文件内核缓冲区当中,这样属性有了内容也有了一个文件不就有了吗?
所以现在在一个磁盘的一个分区当中,我们已经可以找到任意一个文件了,可是我怎么知道我在哪一个分区呢?
- 对于一个分区而言,需要包含一个根目录和若干普通目录
- 同时一个分区必须经过"挂载"到目录当中,才可以被用户以路径的方式进行访问。
如上图,所以我们的一个分区天然就有路径,只要我们进入了某个被挂载的目录我们就相当于进入了某个分区。进入了分区我们就可以任意创建目录和文件。上文讲过任意一个文件都有路径,所以一个路径的前缀就代表着我们处于那个分区。
深刻理解软硬链接
软链接
如上图,ln -s + filename + 软连接之后的名称
就可以为指定的文件创建软链接了,同时软连接是一个独立的文件,有自己的 i n o d e inode inode,它的文件内容存储的是所链接文件的路径。
⚠️: 同时软链接的文件前面的类型为l
。
那么为什么要有软链接呢?
- 如下图
⚠️: 我们建立软链接的时候最好使用绝对路径,不然找不到。
- 作为应用程序的快捷方式 | 快速定位一个文件,以最简单的方式进入。
硬链接
如上图,ln + filename + 硬连接之后的名称
,同时硬链接之后的文件inode和原文件相同,那么右边的数字代表的什么意思呢?
右边的数字其实就是文件的引用计数,当文件为0的时候就会把文件彻底释放掉了。同时硬链接和源文件指向同一个文件,就类似两个指针指向同一片空间。
硬链接的作用:
- 对文件进行备份
- 统计目录当中有多少个子目录
所以我们18-2 == 16就可以计算出/目录下的目录个数。
⚠️:在linux中,不允许对目录创建硬链接(避免环状问题 . 和 . . 除外 .和..除外 .和..除外):
动静态库
动静态库是把我们编译形成的.o文件的集合,就是把所有的.o打包,里面是我们头文件一些函数方法的实现,一般我们都需要跟头文件配合使用。
动态库:程序运行时加载
静态库:直接把函数的方法加载到调用的地方
静态库
1. 手动制作静态库
如上图,我们形成静态库的指令为ar -rc + lib+库名.a(.0.0.1) + 所需要的.o文件
。
− r : r e p l a c e ( 存在时替代 ) − c : c r e a t e ( 不存在时创建 ) -r:\ replace(存在时替代)\ -c:create(不存在时创建) −r: replace(存在时替代) −c:create(不存在时创建) ,同时对于库的名字,必须以 l i b lib lib为开头 . a .a .a结尾,同时.a后面可以增加版本信息(可加可不加)。
2.调用静态库
(1)安装到系统
在 l i n u x linux linux系统中, g c c gcc gcc默认会查找的头文件路径为 / u s r / i n c l u d e /usr/include /usr/include,默认查找的库路径为: / l i b 64 /lib64 /lib64,所以我们只需要把我们的头文件和我们的库放入系统的查找路径底下即可。
(2)自己指定查找路径
当我们不想安装到c标准库中的时候,在我们当前目录下只有库文件和.o文件的时候怎么办呢?那我们就需要指定我们自己的库。
gcc -main.c -l+库名
,其中这个库名前面的 l i b lib lib和后面的 . a .a .a都不加。
同时由于使我们自己写的库, g c c gcc gcc只会去查找自己的默认路径 / u s r / i n c l u d e /usr/include /usr/include和 / l i b 64 /lib64 /lib64,所以我们还需要使用gcc main.c -L + 库所在路径
,让 g c c gcc gcc除了在系统路径下查找之外,还要在我们指定的路径之下查找。
⚠️: 如果我们自己写的库名和系统的库名一样,那么系统先查找到哪个就用哪个。
(3)自己创建include和lib,发布给别人
一般软件都会把自己的代码封装为库,同时源文件放在 i n c l u d e include include目录下,而库文件放入 l i b 目录 lib目录 lib目录下,然后打包让别人下载下来使用自己的产品,我们也来模拟这个过程,只不过我们只是创建一个 o t h e r other other目录。
这里我们要多一个选项:gcc main.c -I + 自己的头文件路径
,这样的话我们 g c c gcc gcc寻找头文件的时候除了会去找默认路径,还会去指定的路径中找头文件。
动态库重复上述操作
⚠️!!!: 在ubuntu20.04环境下,安装到系统也不管用,需要增加环境变量 (方法三)
方法4也可以。
(1)安装到系统
动态库的后缀为.so,同时对比静态库的不同是我们动态库形成可执行程序是用 g c c gcc gcc:
如上图,gcc -o -shared
形成共享文件,gcc -c fPIC
, file Position ignore code(与位置无关码),这有这样才可以形成动态库。
同时由于我们是自己写的库,所以需要特殊指定查找的库。同时对于动态库而言,可以使用ldd+可执行程序
可以查看依赖哪些动态库,如下图:
如上图,使用 f i l e file file + 可执行程序,可以查出来是动态链接还是静态链接
⚠️:这里只要我们把自己写的头文件安装到系统之后,在 m a i n . c main.c main.c中引用头文件就得使用 # i n c l u d e < m y _ s t d i o . h > \#include\ <my\_stdio.h> #include <my_stdio.h>。
(2)自己指定查找路径
这个跟上述静态库一样不做解释,⚠️这里可能会有问题,我们到下一个方法一起说明。
(3)自己创建include和lib,发布给别人
如上图,我们发现此时运行我们的可执行程序,竟然找不到我们的动态库,这是为什么呢?
由于动态库我们是运行的时候加载的,使我们操作系统去加载,而我们前面的 g c c − I − L − l gcc\ -I\ -L\ -l gcc −I −L −l都是告诉编译器去哪找,跟系统没关系,所以此时才会找不到。
解决方法 (环境centos7)
- 安装到系统,见 ( 1 ) (1) (1)
- 在系统路径建立我们库的软链接
- 在linux系统中查动态库,除了去找默认路径,还会去查环境变量-> L D _ L I B R A R Y _ P A T H LD\_LIBRARY\_PATH LD_LIBRARY_PATH。
env中如果没有也没关系,我是因为配置了vim才会有上面这个,此时我们只需要把我们的路径导入这个环境变量即可:
但是我们知道我们自己手动 e x p o r t export export的时候等重启 X S h e l l XShell XShell就会消失,如果你想要一直有,只需要在家目录下的 . b a s h r c .bashrc .bashrc文件中添加自己的路径即可:
- 全局配置路径
echo /home/dpj/linux_code/daily_code/dir > /etc/ld.so.conf.d/test.conf# 在/etc/ld.so.conf.d/创建一个文件(.conf),把当前路径输入到文件当中。
在我们系统会去 / e t c / l d . s o . c o n f . d / /etc/ld.so.conf.d/ /etc/ld.so.conf.d/这个目录下查找加载动态库的配置文件,我们如果想要系统知道我们的动态库位置,也可以在这个目录下创建一个 . c o n f .conf .conf后缀的文件(文件名随意),里面只需要写我们库所在的路径即可。⚠️: 此时我们的系统不是不知道链接哪一个而是找不到位置,所以我们需要给系统我们动态库的位置。
当我们创建完文件之后还需要使用ldconfig
更新一下, 是 L 不是 i 😁 是L不是i😁 是L不是i😁。
上述4中方法中常用的是 ( 1 ) , ( 3 ) (1),(3) (1),(3),其他两个了解一下即可。
注意事项
如上图,当我们动静态库同时存在的时候优先使用动态库。如果我们使用的是动态库,然后我们把动态库删了,就会报错,而静态则不会。
- 如上图使用 − s t a t i c -static −static可以强制使用静态链接。
- 如上图,如果我们只有静态库,但是默认使用动态链接, g c c gcc gcc没得选只能对.a局部进行动态链接,用什么方法拷贝什么方法。
动态库的理解,动态库加载和进程地址空间
动静态库会通过页表映射在进程地址空间中的堆栈之间(共享区)当中,当我们执行正文代码的时候执行到动态库的部分时会跳转到共享区当中然后再跳转回来继续执行。
那如果又有一个进程依赖这个动态库呢?
如上图,当一个新进程也依赖这个库的时候,页表会直接映射内存当中动态库的位置,并且动态库无需再另外加载一次,也就是动态库如果被多个进程所依赖只需要加载一次即可。这也是为什么默认使用动态库的原因,效率高。