动静态库的打包和使用
为什么要有库?
在Linux系统中,软件和程序往往依赖于各种库文件来实现特定的功能。说白了就是用别人的轮子.
平时我们在学习C/C++ 的时候包含的一些头文件实际上就是别人写好的库文件,然后我们写的程序是调用这些库来使用的.
那么既然别人已经写好库了,那我们不可能把.c/.cpp 文件拿过来再 ->预处理 -> 编译 -> 链接 ,这样子就太麻烦了,所以我们一般去调用库的时候都是编译好的.o文件和.h文件,直接在最后一步与我们自己的软件链接就行了
就啊好像你要给汽车装四个轮子,我们不可能买回来的是轮胎轮毂螺帽等等一大堆零件分开要自己装上去的,肯定是卖家打包好的轮子和说明书一起卖给我们,我们直接按照方法装在车上就可以使用了
这就是库文件的作用
而库文件大致可以分为两类:静态库(Static Libraries)和动态库(Dynamic Libraries)。
一般gcc默认使用动态链接
,若个别库只提供静态库的话,只能局部性的把指定的静态库进行静态链接,其他库进行正常的动态链接
因此,首先我们需要头文件.h 库文件的原型.c 和 测试代码test.c
Add.h
#pragma once
extern int Add(int x,int y);
Sub.h
#pragma once
extern int Sub(int x ,int y);
Add.c
#include"Add.h"
int Add(int x , int y)
{
return x+y;
}
Sub.c
#incldue"Sub.c"
int Sub(int x ,int y)
{
return x-y;
}
test.c
#include<stdio.h>
#include"Add.h"
#include"Sub.h"
int main()
{
int x = 100;
int y = 10;
printf("Add: %d \n",Add(x,y));
printf("Sub: %d \n",Sub(x,y));
return 0;
}
静态库
原理: 静态库是一种编译链接方式,其中库的代码在编译时直接被复制
到最终的可执行文件中。因此被称为静态库
!它通常有.a
扩展名(Archive),由一系列的对象文件(.o)组成,通过ar
(archive)工具创建。使用静态库编译的程序,运行时不需要该库文件,因为所有需要的库函数已经被整合到了可执行文件内。
打包和使用:
- 打包静态库: 假设有两个源文件
Add.c
和Sub.c
,首先将它们编译为对象文件:
然后,使用gcc -c Add.c Sub.c
ar
工具将这些对象文件打包为静态库libmyAS.a
:
这里注意,库文件的命名方式是 lib***.a , 其中lib 和 .a 是库文件的前缀和后缀,因此这个库文件的真正名字实际上是myAS,
这里,ar rcs libmyAS.a Add.o Sub.o
r
代表替换现有的库文件,c
代表创建库文件(如果它不存在的话),s
代表添加索引到库文件。这样我们就会创建出.a库方法
而这只是初步的步骤,最终我们需要把一个完整的库,包含头文件和库方法,相当于要把说明书也带上,因此我们大部分头文件都是公开的,因此一般都是直接打包头文件和库方法文件,把头文件放在include 文件中 ,把库方法放在lib文件中,
为此可以直接使用Makefile打包(在编写Makefile文件的时候应该把.h和.c文件都包含进来,等打包好库文件后只需要把库文件交给别人即可):
static-lib=libmyAS.a
$(static-lib):Add.o Sub.o test.o
ar -rc $@ $^
#因为还没有.o文件,因此先把.c文件编译成.o文件
%.o:%.c
gcc -c $@ $<
#把库文件和头文件打包到一个文件夹中
.PHONY:output
output:
mkdir -p myAS_lib/include
mkdir -p myAS_lib/lib
cp *.h myAS_lib/include
cp *.a myAS_lib/lib
.PHONY:clean
clean:
rm -rf *.o *.a myAS_lib
在进行make和 make output 后,我们得到了一个myAS_lib库:
这样静态库就打包完成了,我们把库和可执行文件放在同一目录下:
- 使用静态库: 在编译程序时,链接静态库:
gcc test.c -I myAS_lib/include -lmyAS -L. myAS_lib/lib
-I
是告诉编译器在myAS_lib/inlcude目录
下查找头文件,-L.
告诉编译器在myAS_lib/lib目录
下查找库文件,-lmyAS
指定链接库libmyAS.a
(注意省略了前缀lib
和扩展名.a
)。
此时就会生成可执行文件a.out
编译:
动态库
原理: 动态库(或共享库),文件扩展名通常为.so
(Shared Object),在程序运行时被载入内存。与静态库不同,动态库不会被复制到可执行文件中,而是在运行时由动态链接器(如ld-linux.so)加载。这意味着多个程序可以共享同一动态库的单个副本,减少了磁盘和内存的使用,同时也简化了库的更新过程。
打包和使用:
- 创建动态库:
在创建动态库前,我们需要把.c文件编译成.o文件,动态库的编译下需要多增加一个-fPIC
命令
gcc -fPIC -c Add.c Sub.c
这个-fPIC的命令的主要原因是为了确保生成的目标代码是位置无关的,从而使得动态库可以被加载到任意内存地址而不需要重新编译。这在动态链接时尤其重要,因为动态链接器会将动态库加载到进程的内存空间中的任意位置。
使用-fPIC标志生成的目标代码是位置无关的,因为它使用相对寻址而不是绝对寻址,这样在加载时就可以正确地解析符号和地址。这使得动态库可以被多个进程共享,并且在内存中的位置可以灵活地调整,而不会引起地址冲突或其他问题。
总之,使用-fPIC标志生成动态库可以增加库的灵活性和可移植性,确保它可以在各种环境中正确加载和运行。
使用gcc
的-shared
选项创建动态库:
gcc -shared -o libmyAS.so libAdd.o libSub.o
这将创建一个名为libmyAS.so
的动态库文件。其中.so是动态库的文件后缀
同样,动态库也需要进行头文件和库文件的打包:
Makefile:
dy-lib=libmyAS.so
$(libmyAs.so):Add.o Sub.o
gcc -shared -o $@ $^
%.o:%.c
gcc -fPIC -c $<
#打包
.PHONY:output
output:
mkdir -p myAS_lib/include
mkdir -o myAS_lib/lib
cp *.h myAS_lib/include
cp *.so myAS_lib/lib
.PHONY:clean
clean:
rm -rf *.so *.o myAS_lib
此时我们的动态库也就打包完成了
-
使用动态库: 编译时指定动态库:
gcc test.c -I myAS_lib/include -lmyAS -L. myAS_lib/lib
-I
是告诉编译器在myAS_lib/inlcude目录
下查找头文件,-L.
告诉编译器在myAS_lib/lib目录
下查找库文件,-lmyAS
指定链接库libmyAS.a
(注意省略了前缀lib
和扩展名.a
)。但是!!!
这样对编译好的可执行程序来说是没有用的,因为动态库是系统进行链接的,告诉编译器是没法运行动态库的
此时有以下几种方法可以让系统找到:
-
- 把头文件和库文件直接分别复制到 /usr/include/ 目录 和 /lib64/ 目录下
sudo cp *.h myAS_lib/include/ /usr/include/
sudo cp *.so muAS_lib/lib/ /lib64/
这样系统就有头文件了,并且可以链接我们的库,编译:
gcc test.c -lmyAS
-
- 建立一个软连接并加上.so后缀
ln -s ./myAS_lib/lib/libmyAS.so libmyAS.so
此时当前目录下就会有一个链接着动态库的软连接
当然,也可以直接在/lib64/目录下创建这个软连接
sudo ln -s ./myAS_lib/lib/libmyAS.so /lib64/libmyAS.so
查看动态库:
运行:
-
- 加载动态库到环境变量中
通过设置环境变量LD_LIBRARY_PATH
指向库文件的目录同样可以使得程序运行
- 加载动态库到环境变量中
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/cris/mytestlib/test/myAS_lib/lib
运行a.out:
但是这种环境变量的配置只是内存及的,重启之后还需要重新配置
-
- 直接更改系统中动态库的配置文件
首先在系统目录下创建一个我们自己的配置文件
- 直接更改系统中动态库的配置文件
sudo touch /etc/ld.so.conf.d/myAS.conf
然后编辑这个配置文件,把动态库的目录写入
sudo vim /etc/ld.so.conf.d/myAS.conf
进行编辑:
/home/cris/mytestlib/test/myAS_lib/lib
完成之后进行配置目录的刷新
sudo ldconfig
加载完成,运行:
下载第三方库怎么使用
我们下载的第三方库(即 yum 安装后)和我们自己写的一样,都是包含了头文件和库方法,因此只需要在我们自己写的程序目录下对库进行连接即可:
gcc test.c -lotherlib
上方代码中的otherlib是我们下载的库的名字
动静态库的区别
- 部署和更新: 静态库被整合到应用程序中,使得程序体积增大,但部署简单。动态库在多个程序间共享,便于更新和减少资源占用。
- 运行时依赖: 使用静态库的程序不依赖外部库文件。动态库需要确保运行环境有正确版本的库。
- 性能: 静态库的程序启动速度可能更快,因为避免了运行时的库加载过程。但动态库节省了内存,特别是多个程序共享相同库时。