Linux制作和使用动静态库

本文介绍了Linux系统中动态库(.so)和静态库(.a)的概念,包括它们的制作与使用方法。动态库在运行时加载,节省磁盘空间,多个程序可共享;静态库在编译时链接,不依赖库,但程序体积大。动态链接和静态链接各有优缺点。文章还详细阐述了如何生成和使用静态库与动态库的步骤,并探讨了动态库和静态库的选择问题。

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

文章目录

  • 一、概念
    • 1.1 动态库和静态库
    • 1.2 动态链接和静态链接
  • 二、制作第三方库
    • 2.1 生成静态库
      • ① 制作静态库
      • ② 使用静态库
    • 2.2 生成动态库
      • ① 制作动态库
      • ② 使用动态库
  • 三、相关题目

一、概念

1.1 动态库和静态库

静态库与动态库本质都是一堆目标文件(xxx.o)的集合,库的文件当中并不包含主函数而只是包含了大量的方法以供调用。将程序编写成库提供给第三方使用,这样做的好处是不会造成源码泄漏,而且调用者不用关心内部实现。

  • 静态库(.a):程序在编译链接的时候把库的代码连接到可执行文件当中,运行时不再需要静态库。Linux中:前缀为"lib",后缀".a";Windows中:后缀".lib"。
  • 动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。Linux中:前缀为"lib",后缀".so";Windows中:后缀".dll。多个程序可以共享使用动态库代码。

ldd + 文件 显示可执行程序依赖的库

库名:去掉前缀lib 和 后缀及后面的版本号,才是库的名字。比如:libc.so.6,这个库为c动态库

一般服务器,可能没有内置语言的静态库,只有动态库,静态库需要我们手动安装。百度 yum安装C/C++静态库 sudo yum install glibc-static

静态库

静态库是程序在编译链接的时候把库的代码复制到可执行文件当中的,生成的可执行程序在运行的时候将不再需要静态库,因此使用静态库生成的可执行程序的大小一般比较大。

动态库

动态库是程序在运行的时候才去链接相应的动态库代码的,多个程序共享使用库的代码。一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码。

在可执行文件开始运行前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中,这个过程称为动态链接。动态库在多个程序间共享,节省了磁盘空间,操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用,节省了内存和磁盘空间。

image-20221009123054809

1.2 动态链接和静态链接

  • 动态链接:在可执行程序运行前,操作系统将使用到的动态库里的机器码从硬盘加载到内存中。gcc默认编译生成二进制程序的方式是动态链接。
  • 静态链接:可执行程序编译链接时,将代码使用到的静态库的代码拷贝到可执行程序中。在gcc编译命令后面加上 -static

查看链接方式:file + 可执行程序

静态链接和动态链接的优缺点:

静态链接:可执行程序文件体积大,特别是当有多个静态程序同时加载而这些静态程序使用的都是相同的库,这时在内存当中就会存在大量的重复代码。但是不依赖库,可移植性强。

动态链接:库执行程序文件体积小,多个用到相同动态库的程序同时运行时,库文件会通过进程地址空间进行共享,内存当中不会存在重复代码。但是依赖库,如果库被删除,该程序也就不可以使用了。

二、制作第三方库

2.1 生成静态库

① 制作静态库

静态库的本质:将所有的.o文件打包

静态库的生成有两个阶段:

  1. 用gcc -c 指令把源文件编译成.o的二进制文件
  2. 通过ar -rc 指令将将.o文件编译成静态库
    ar是gnu归档工具,rc表示(replace and creat)

image-20221009124249948

ar -tv 静态库,查看静态库内容,即包含了哪些二进制文件

  • t:列出静态库中的文件
  • v:verbose 详细信息

② 使用静态库

image-20221009124635438

lib下面有头文件和打包起来的库文件

命令行下去找静态库要有以下的命令

-I(大写i)+路径 指明头文件搜索路径

-L+路径 指明库文件搜索路径

-l(小写L)+库名称 指明要链接哪一个库,库名为去掉库文件前的lib和后缀就是库名。

gcc test.c -I./lib -L./lib -lmymath

我们之前写的C语言代码也用了库,但是为什么我们编译的时候不需要加上这些指令呢?

因为之前的库,在系统的默认路径下:/lib64,/usr/lib,/usr/include等。编译器能识别这些路径

换句话说,如果我不想带这些选项,我是不是可以把对应的库和头文件拷贝到默认路径下,没问题!但是极其不推荐,会污染别人写的文件!

上面的过程,也就是一般软件的安装过程!把人家的头文件和库下载到默认路径里

总结

我们交给别人的其实就是一个库文件+一批头文件

编写一个静态库:

  • 将源文件生成目标文件
  • 将所有目标文件打包成库文件。ar -rc 库文件名 目标文件

库要包含头文件打包的库文件。头文件可以直接获得。

使用静态库:

  • 编译的时候加上路径。

2.2 生成动态库

① 制作动态库

  • -shared:表示生成共享格式,用于最后生成可执行程序,该选项要在 -o前面。

    生成一个动态链接的共享库

  • -fPIC:产生位置无关码,用于编译生成 .o 目标文件,该选项在-c前面。

    程序内部的地址方案是:与位置无关,就是库文件可以在内存的任何位置加载,而且不影响和其他程序的关联性

  • 动态库名称规则:libxxx.so

image-20221009125341336

output就是我们的发布文件目录

② 使用动态库

image-20221009125628971

通过上面的这种方式我们可以成功的编译出可执行程序,但是当我们运行时会发现,这个程序不能运行:

[yzy@VM-4-4-centos friend]$ ./test 
./test: error while loading shared libraries: libmymath.so: cannot open shared object file: No such file or directory

因为上面只是告诉 编译器 头文件和库在哪里,当程序编译完成之后,就已经和编译无关了。

运行可执行程序的是加载器,我们要在运行程序的时候,进一步告知系统动态库在哪里。

解决方法

①动态库拷贝到系统默认路径下

推荐

LD_LIBRARY_PATH环境变量 -》 指明程序启动之后,动态库的搜索路径

echo $LD_LIBRARY_PATH查看

export LD_LIBRARY_PATH = 动态库所在路径,导入搜索路径

image-20221009130442223

将环境变量导入到~/.bashrc,不推荐,如果一定要改就多开几个终端防止写错

④全局配置

让环境变量每次都生效,/etc/ld.so.conf.d 搜索动态库的一个系统路径,我们可以进入到该文件夹里面添加路径

创建一个 XXX.conf文件,然后打开这个文件,往里面添加路径

image-20221009131156450

总结:

编写一个动态库:

  • 获得源文件和头文件
  • 将源文件生成目标文件,gcc -fPIC -o
  • 目标文件生成静态库,gcc -share -o *.so *.o

动态库:就是所有头文件和库文件

使用动态库:

  • 编译时要让编译器知道库文件路径,头文件路径和库名
  • 让系统知道库文件路径。添加环境变量。将库文件绝对路径加到LD_LIBRARY_PATH中。

如果只提供静态库,我们只能将我们的库,静态链接到我们的程序中

如果只提供动态库,我们只能将我们的库,动态链接到我们的程序中,如果加上 -static,也不能静态链接!

如果两种都想实现,一般需要提供两种版本的库文件!gcc 、g++优先链接动态库。

为什么我们之前写的所有代码都没有报错?默认是动态链接,因为我们一定有动态库!-》因为系统中有很多命令是C语言的写的,而且是动态链接的!

三、相关题目

1、下面哪一个不是动态链接库的优点?

A.共享

B.装载速度快

C.开发模式好

D.减少页面交换

解析

答案解析

动态链接链接的是动态库,而动态库中包含了大量的常用的功能接口指令代码

这种链接方式,是用于解决静态库存在的浪费内存和磁盘空间,以及模块更新困难等问题。

动态链接生成可执行程序,可执行程序中会记录自己依赖的库列表以及库中的函数地址信息,等到运行程序的时候,由操作系统将库加载到内存中(多个程序可以共享,不需要加载多份相同实例),然后根据库加载后的地址在对每个程序内部用到的库函数的地址进行偏移计算。

基于这么一种思想,动态链接具有以下优缺点:

优点

  • 更加节省内存并减少页面交换;
  • 库文件与程序文件独立,只要输出接口不变,更换库文件不会对程序文件造成任何影响,因而极大地提高了可维护性和可扩展性;
  • 不同编程语言编写的程序只要按照函数调用约定就可以调用同一个库函数;
  • 适用于大规模的软件开发,使开发过程独立、耦合度小,便于不同开发者和开发组织之间进行开发和测试。

缺点

  • 运行时依赖,否则找不到库文件就会运行失败
  • 运行加载速度相较静态库慢一些
  • 需要对库版本之间的兼容性做出更多处理

根据以上理解,题目为选择错误选项,因此选择B选项。


2、关于静态库与动态库的区别,以下说法错误的是()

A.加载动态库的程序运行速度相对较快

B.静态库会被添加为程序的一部分进行使用

C.动态库可用节省内存和磁盘空间

D.静态库重新编译,需要将应用程序重新编译

解析

动态库也叫运行时库,是运行时加载的库,将库中数据加载到内存中后,每个使用了动态库的程序都要根据加载的起始位置计算内部函数以及变量地址,因此动态链接动态库加载及运行速度相较静态链接是较为不如的,但是它也有好处,就是多个程序在内存中只需要加载一份动态库就可以共享使用。

静态链接,链接静态库,每个程序将自己在库中用到的指令代码单独写入自己可执行程序中,程序运行时无依赖,加载运行速度快,但是程序运行后有可能会有冗余代码在内存中

根据以上理解分析:

  • A错误 加载动态库的程序运行速度相对较慢,因为动态库运行时加载,映射到虚拟地址空间后需要重新根据映射起始地址计算函数/变量地址
  • B正确
  • C正确
  • D正确 动态链接的程序一旦库中代码发生改变,重新加载一次动态库即可,但是静态链接代码是写入程序中的,因此库中代码发生改变,必须重新链接生成程序才可以

而题目为选择错误选项,因此选择A选项


3、关于动态库和静态库以下描述正确的有()

A.gcc test.c -o libmytest.so 命令可以生成一个动态库

B.库文件中可以包含main函数

C.静态库使用gcc命令生成

D.库文件的链接可以使用-L选项指定所在路径,使用-l命令链接指定的库文件

答案解析

A选项错误,默认情况下,gcc test.c -o libmytest.so 生成的是一个可执行程序,而并非动态库, 若要生成动态库需要使用 -shared 选项进行指定

B选项错误,库文件是被其他程序引入使用的,因此不能有main函数,否则会与程序中的main函数产生冲突

C选项错误,静态库使用 ar 指令生成

D选项正确,gcc的-L选项用于指定链接库路径, -l选项用于链接指定库文件


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Morning_Yang丶

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

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

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

打赏作者

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

抵扣说明:

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

余额充值