【Linux系统】动静态库

目录

一.制作和使用静态库

1.制作静态库

2.使用静态库

(1)编译时链接库的gcc指令

(2)第一二三方库

(3)ldd指令 

(4)编译链接时加不加-static的区别

(5)整合头文件和库文件

(6)头文件不在当前目录下时的编译方法

 二.制作和使用静态库

1.制作动态库

2.使用动态库

(1)编译链接

(2)运行可执行程序

三.动态库加载

1.可执行程序的地址

2.绝对编址与相对编址

3.程序和库的加载过程

4.调用自己的函数vs调用库函数

5.共享库


一.制作和使用静态库

1.制作静态库

库中的方法将方法编译形成.o文件,方法声明放在头文件。用户如果想要使用库中的方法,只需在自己写的源文件中#include提供的头文件,编译自己写的源文件形成.o文件,再和提供的.o文件链接,形成可执行程序。

 例如main.c是我自己写的源文件,其它头文件和.o文件是别人提供的,我想使用别人的写的方法,只在main.c中包含给的头文件,编译成.o文件,再和别人的.o文件链接形成可执行文件。

但是这样写真的很麻烦,如果有多个目标文件,链接时每个都要写出来效率也太慢了。因此将所有.o文件打包起来,形成库文件。

静态库原理:

静态库就是将库中的源代码编译成为.o目标二进制文件,然后打包

制作过程:

编译源文件形成目标文件,然后将目标文件打包形成.a静态库

 

2.使用静态库

(1)编译时链接库的gcc指令

当前目录下只有我写的源文件main.c,对它编译报错,gcc找不到add.h这个头文件。所以我们应该将头文件和库都放在当前目录下。

 将头文件和库文件都拷贝到当前目录下,编译还是报错:找不到方法的定义,说明gcc并没有链接mymath。编译器不认识mymath这个库,因为它是第三方库。对于这种第三方库,需要我们编译时指定名称。

gcc main.c -l mymath -L .   //-l 指定库的名称,库的名称要去掉前缀lib,去掉后缀.a

                                       //-L 指定库的路径,对于静态库,即使放在当前路径下也要指定路径

(2)第一二三方库

  1. 第一方库指写操作系统的程序员写的库
  2. 第二方库指C语言的库clib
  3. 第三方库是其它程序员写的库
  4. 第一方二方库gcc是认识的,所以我们之前编译时从来没有使用过-l -L这样的选项。但是第三方库gcc是不认识的,所以我们必须用-l指明要链接哪个库,必要时用-L指明库的路径,比如静态库放在当前路径下也要指明路径。

(3)ldd指令 

ldd a.out   //ldd用于查询可执行程序依赖的库(动态库)

查询结果并没有mymath,因为mymath是静态库, 编译时就已经将代码拷贝到可执行程序中了。ldd中的d是“dynamic“,只能查询静态库 

(4)编译链接时加不加-static的区别

不带-static,有动态库就链接动态库,如果某个库只提供了静态库,就链接静态库。->尽量链接动态库

带-static,所有库必须都静态链接,如果没有提供静态库就报错 ->只链接静态库

(5)整合头文件和库文件

将头文件和库文件都放在用户自己写的源文件下显得很乱,用户也容易误删,所以应该将头文件和库文件归类放在相应目录下

(6)头文件不在当前目录下时的编译方法

 方法一:更改源文件,include头文件时指明路径(非常不推荐)

方法二:编译时加上-I(i的大写!!!)选项,增加头文件搜索路径

gcc -I mymath_lib/include -l mymath -L mymath_lib/lib 

//-I指明新增的头文件搜索路径,即在系统默认搜索路径/usr/include,当前路径和mymath_lib/include路径下搜索头文件

方法三:将头文件拷贝到系统默认搜索路径下

编译时就不需要带上-I选项

同理,库文件也可以安装到默认搜索路径

编译时就就不需要带上-L选项了

 二.制作和使用静态库

1.制作动态库

静态库原理和动态库原理一样,先形成.o文件,然后打包所有.o文件。

动态库比静态库更加常用,gcc编译器带有制作动态库的方法,不需要借助其它工具

 -fPIC ——产生位置无关码

-shared——形成共享库(动态库)

2.使用动态库

(1)编译链接

编译时链接动态库和静态库的方法是一样的,都是-I -l -L三个选项

(2)运行可执行程序

运行可执行程序必须同时找到可执行程序和依赖的动态库,程序和动态库都要加载到内存。

系统只会在当前路径和库的默认搜索路径下查找,所以必须让系统找到库在哪里

方法一:将库文件拷贝到/lib64目录下

方法二:在当前目录或者系统的默认搜索路径下建立所依赖库的软链接(注意软链接的名字和库文件名字相同)

方法三:更改环境变量LD_LIBRARY_PATH,使系统找到动态库(想要永久保存,需要更改系统登录时导入环境变量的配置文件)

方法四:更改系统关于动态库的配置文件,将库所在路径写入配置文件,写完后刷新ldconfig

三.动态库加载

1.可执行程序的地址

磁盘上的可执行程序在加载到内存之前内部也是有地址的。我们在编写代码时使用的是变量名,函数名等符号,但编译完的代码使用的都不是这些符号,而是代码和数据的地址,例如访问某个变量就是访问某个地址的空间,调用某个函数是跳转到函数的起始地址去执行。这些地址的编排规则和虚拟地址空间是一致的。

所以可执行程序也是分为代码段,数据段,栈区,堆区等区域,此外还多了一个符号表,里面记录了所有符号(全局变量,函数)对应的地址。

正是因为可执行程序的地址编排和遵守虚拟地址空间那一套规则,所以将磁盘上的文件加载到内存,操作系统为进程安排地址空间,建立页表映射时就省去不少麻烦。

2.绝对编址与相对编址

可执行程序采用绝对编址的方式安排地址,即00000000~FFFFFFFF,动态库采用相对编址,即相对于库的起始地址的偏移量。

因为库是不会单独加载到内存运行的,它是供其它进程访问的,所以并不需要将它按照虚拟地址空间的方式编址

由于动态库代码在编译时期并没有合并到可执行程序内部,可执行程序内仅仅记录了要调用的方法在哪个库的多少偏移量处。进程运行时,它所依赖的动态库也必须加载到内存,库被加载到内存后,它的起始地址也就能确定了,起始地址加上编译时期记录的偏移量,就能确定库方法的地址了。

由于动态库内的地址是相对地址,它的起始地址是在加载到内存后才确定的,所以动态库被加载到不同的位置没有关系,这种通过相对编址方式编出来的地址就叫做位置无关码

没有加载静态库这一说,因为在链接阶段,静态库的代码就被拷贝到可执行程序的代码段了,会统一进行绝对编地址,以后再加载时和静态库没有任何关系了。可以将静态库就看成你写的代码,只不过你写的代码还要从.c编译成.o才能链接,而静态库省去了编译这一步骤。

3.程序和库的加载过程

库被加载到内存之后,要被映射到指定使用了该库的进程们的地址空间的共享区部分,库在共享区的位置是任意的,但是一旦建立映射关系后,库的起始地址就确定了,操作系统会将这个起始地址记录下来,起始地址+偏移量就能找到库方法。

4.调用自己的函数vs调用库函数

自己的函数在代码段,库函数在共享段,调用自己的函数在代码段内跳转,调用库函数在代码段和共享区之间跳转,但是二者都是在进程的虚拟地址空间内跳转。

5.共享库

在你启动某个进程之前,进程依赖的某个动态库可能已经在内存里了,因为其它运行的进程也有可能依赖这个库。此时操作系统无需重复加载该库,只需为新启动的进程,建立虚拟地址空间的共享区和物理内存的映射关系即可。动态库是被多个进程共享的,所以动态库也叫作共享库。

顺便解释一下,静态库是指库代码相对于程序其它代码的位置是固定不变的,永远都在代码段的某个固定位置,动态库指库代码在可以加载到共享区的任意位置

  • 20
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值