动静态库-动态库加载

本文详细介绍了静态库和动态库的创建、使用方法,包括链接选项、库路径指定、库文件命名以及动态库加载原理。特别关注了如何处理头文件和库文件的位置、链接过程中的注意事项和动态库加载到进程地址空间的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

编译工具gcc时,讲到链接库,其中说静态链接和动态链接的相关知识,并且介绍了一些拓展命令,eg:od/file/ldd/readelf

接下来,目标:深入理解动静态库

引入

当我们想把我们写的代码给别人使用有两种方式:

  1. 直接把我们写的源文件和头文件给别人。
  2. 把源代码打包成库,加上头文件给别人。(库+.h)

优劣:

  • 第一种:相当于把底层的东西直接暴露给使用者。
  • 第二种:相当于只把使用说明(头文件)展示给使用者,使用者只知道如何调用方法,而不知道底层具体如何实现。

一、静态库

1. 创建静态库

①原理

生成库

  1. 静态库其实就是一堆.o文件打包形成
  2. 要先把源文件提前编译成目标文件
  3. 所以生成的库,人是读不出来的。

②创建

创建静态库

2. 使用静态库

开发者的库(xxx.a) + 使用者编写的代码(xxx.c) = 可执行程序(xxx.exe)

①借助编译选项

测试代码:
测试代码

  1. 直接编译一下试试:
    运行结果
    结果:运行失败
    结论:我们这里测试的头文件,不在系统指定的目录下(usr/include),所以gcc找不到这个头文件.
  1. 告诉编译器,头文件的位置。使用-I选项,后面跟头文件所在路径即可
    命令:gcc main.c -I ./lib/include
    运行结果
    结果:运行失败
    结论:这里不再是找不到头文件了,使用我们指定头文件路径这个是正确的,但是结果中的报错,是变量未定义报错,那很明显是链接报错,找不到静态库
  1. 告诉编译器,库的位置。使用-L选项,后面跟库文件所在路径即可
    命令:gcc main.c -I ./lib/include -L ./lib/mymathlib 运行结果
    结果:运行失败
    结论:指定库文件路径依旧不行,因为还要指定库文件名。为什么头文件就可以不指定文件名,那是因为在main.c中已经包含了头文件名
  1. 告诉编译器库的名称。使用-l选项,后紧跟库名。注:库名称是去掉lib和.a后缀。libmymath.a:mymath就是库名
    命令:gcc main.c -I ./lib/include -L ./lib/mymathlib -lmymath
    运行结果
    运行结果:
    运行结果

②只需要带库名

方法:

  1. 把头文件和库放到系统指定路径下,就可以只带-l编译选项。其实这个动作就是库的安装
  2. 头文件和库在系统指定的目录下,建立软链接,就可以只带-l编译选项(这个下面演示一下)

演示:给头文件和库在系统指定目录下建立软链接

  1. 对头文件建立软链接 对头文件建立软链接
    • 查看系统指定目录下的软链接:建立软链接成功 软链接
    • 因为是在/usr/include目录下建立了一个路径的软链接,所以在测试使用的要包含头文件的方式要改变
      头文件包含
  • 运行程序
    命令:gcc main.c -L ./lib/mymathlib -lmymath 注意:因为头文件的软链接被放到系统的指定目录下,所以就不需要使用-I选项
    运行结果:
    运行结果
  1. 对静态库建立软链接 对静态库建立软链接
  • 运行程序
    命令:gcc main.c -I ./lib/include -lmymath 注:因为库的软链接被放到系统的指定目录下,所以就不需要使用-L选项
    运行结果:
    运行结果

注:把头文件和库在系统指定的目录下,一起建立软链接,就可以只适用-l选项。上面的演示,我没有放在一起,所以要么带L选项,要么就要带I选项

3. 小结

使用总结:

  1. 大写i(I)指定头文件所在路径
  2. 大写l(L)指定库所在路径
  3. 小写l(l)指定库名称。注:库名称是去掉lib和.a后缀

静态库总结:

  1. 把头文件和库都移动到系统的指定目录下就可以不加选项I和L,但是只要使用第三方库必定要使用gcc -l[库名](第一二方,可以理解为系统和语言层次的库)
  2. 在系统指定目录下建立软链接也可以不加选项I和L
  3. 理解库中的全局变量
  4. 如果系统只提供静态库,则编译器就只能进行静态链接
  5. 可以链接多个库

二、动态库

1. 创建动态库

创建动态库的两个关键命令

  1. 生成目标文件:gcc -fPIC -c $^
  2. 生成动态库:gcc -shared -o $@ $^

生成动态库,并且分类:
创建动态库

注:发现生成的动态库具有可执行权限 生成的动态库
虽然具有可执行权限,但是并不能执行
可执行权限:以可执行程序的方式加载到内存

2. 使用动态库

测试代码:
测试代码

  1. 编译:根据对静态库使用的编译选项,指定路径测试动态库
    命令:gcc test.c -I ./mylib/include -L ./mylib/lib -lmymethod
    编译
    结果:编译成功,生成了可执行程序a.out
  2. 运行:
    运行结果
    结果:运行失败,没有发现共享库

注:ldd命令:显示可执行程序链接的动态库

  1. 原因:
    • 因为是动态库要加载到内存。编译器确实知道了动态库的位置,但是我系统不知道——加载器
  2. 解决:
    • 拷贝到系统默认的库路径/lib64 or /usr/lib64(最常用,我们以后使用的库,都是别人成熟的库,所以可以直接安装都系统指定的目录下)
    • 在系统默认的库路径(/lib64 or /usr/lib64)下建立软链接
    • 将自己的库所在的路径,条件到系统的环境变量LD_LIBRARY_PATH中
    • /etc/ld.so.conf.d建立自己的动态库路径的配置文件,然后加载(ldconfig)

这里测试一下后面两种解决办法,前面俩种拷贝和建立软链接就不测试了

  1. 环境变量:
    解决
    • 注:重启Xshell之后,环境变量就恢复了,所以如果想一直可以,就要在配置文件中进行添加
  2. 建立自己的动态库路径的配置文件
    解决
    • 注:
      1. 添加配置文件操作需要在root账号下
      2. 进入/etc/ld.so.conf.d目录下
      3. 创建一个以.conf为后缀的文件,名字任意
      4. 文件内填入需要使用的动态库路径
      5. ldconfig更新

如果还要添加别的路径下的动态库,需要再创建文件然后写入路径

注:外部库很多,eg:ncurses库——基于终端的图形界面库(可以上网搜下载一下玩玩)

三、 动态库加载原理——进程地址空间

  1. 动态库在进程运行的时候,是要被加载的
  2. 常见的动态库被所有的可执行程序使用(eg:c标准库就被Linux很多指令共享使用)。动态库 —— 共享库

所以动态库在系统加载后,会被所以进程共享

1. 地址

①程序没有被加载前的地址

问题:程序在编译好之后,没有运行前,内部有地址吗?

通过命令,可以把编译好的程序反汇编出来
命令:objdump -S a.out
在这里插入图片描述

所以程序编译好之后,内部就有地址了,而且现在的编译器大多会采用平坦模式。这个地址就是虚拟地址,更准确的说是逻辑地址,对于目前来说,二者并没有什么区别,所以还没加载到内存,这个虚拟地址已经出现了
注:
平坦模式:内存管理模式,它将整个内存地址空间视为一个连续的线性地址空间,从0递增,程序可以直接使用线性地址进行访问

②程序加载后的地址

把可执行程序加载到内存,必然要先形成好自己的PCB
图解:
图解

2. 原理

①动态库的地址

图解:
图解

所以也得出为什么采用fPIC(产生位置无关码),因为直接用偏移量对库中的函数进行编址。而静态库就不需要这样做,因为静态库是直接拷贝到可执行程序中去的,直接编址就可以

②原理

图解:
图解

  1. 结论:虚拟地址和物理地址建立映射,从此执行任何的代码,都是在我们的进程地址空间中执行的
  2. 事实:系统在运行的时候,一定存在很多个动态库。所以OS就得管理起来 —— 先描述再组织。所以系统中所有库的加载情况,OS都清楚
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kpl_20

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值