> 文档中心 > 【Linux篇】第十篇——动静态库(动静态库的介绍+动静态库的打包与使用)

【Linux篇】第十篇——动静态库(动静态库的介绍+动静态库的打包与使用)


⭐️上一篇博客介绍了基础IO相关的一些内容。这篇博客要继续聊一聊关于动静态库相关的一些内容。在之前的学习过程中,我们也会使用C/C++的相关的库,这些库都是由不同的语言打包好了的,我们直接调用即可。下面我们就会好好聊一聊相关内容。

目录

  • 🌏动静态库的概念
  • 🌏静态库的打包与使用
    • 🌲静态库的打包
  • 🌏静态库的使用
  • 🌏动态库的打包和使用
    • 🌲动态库的打包
    • 🌲动态库的使用
  • 🌐总结

🌏动静态库的概念

静态库: Linux下,以.a为后缀的文件。程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库。本质是在编译时把静态库中的代码(不是一次性加载,而是分页加载)复制到了进程的的代码区中。
动态库: Linux下,以.so为后缀的文件。程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码。
静态链接: 将库中的相关代码复制进可执行程序中的过程。
动态链接: 在可执行文件开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中。
链接的本质: 链接.o文件和.lib文件
库文件名称: 比如libc.so,去掉前缀lib和后缀.so,剩下的就是库名
在这里插入图片描述
实例演示: 分别使用静态链接和动态链接编译生成两个可执行程序,比较两个程序的大小
使用gcc静态链接编译时,命令要带上**-static** 选项,如下:

gcc -o mytest-s mytest.c -static

如果执行命令报错,可以先后执行下面两条指令:
1

 yum install glibc-static

2

yum install glibc-static libstdc++-static

看下面两个可执行程序,mytest和mytest-s分别是动态链接和静态链接生成的:
在这里插入图片描述
可以看到的是,使用静态库静态链接成的可执行程序比动态链接生成的可执行程序要大很多。
我们还可以通过file命令查看文件的链接属性:
在这里插入图片描述
还可以通过ldd 命令查看可执行程序的依赖库,动态链接生成的可执行程序才有依赖库,静态链接升序的可执行程序不依赖任何库文件,因为库文件的代码已经复制进可执行程序了。
在这里插入图片描述
总结动静态库的优缺点
静态库

  • 优点: 程序运行的时候将不再需要静态库
  • 缺点: 生成的可执行程序比较大。如果多个使用静态链接生成的程序同时运行会占用大量的内存空间

动态库

  • 优点: 动态链接使得可执行文件更小,节省了磁盘空间。操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用,节省了内存和磁盘空间
  • 缺点: 程序运行的时候依赖动态库

🌏静态库的打包与使用

🌲静态库的打包

静态库打包: 本质其实就是将代码编译成.o的二进制文件,然后进行打包。
为了更好地演示这个过程,我创建了myadd.cmyadd.hmysub.cmysub.h四个文件,内容分别如下:
myadd.h

#ifndef __MYADD_H__#define __MYADD_H__int MyAdd(int x, int y);#endif

myadd.c

#include "myadd.h"int MyAdd(int x, int y){  return x+y;}

mysub.h

#ifndef __MYSUB_H__#define __MYSUB_H__int MySub(int x, int y);#endif

mysub.c

#include "mysub.h"int MySub(int x, int y){  return x-y;}

如下:
在这里插入图片描述
打包静态库的步骤:

  1. 先将myadd.cmysub.c 变成生成对应的二进制文件
    在这里插入图片描述

  2. 使用ar 归档工具对两个二进制文件进行打包,同时带上选项**-rc**(r和c分别代表replace和creat),这里的库名是mymath

ar -rc libmymath.a *.o

【Linux篇】第十篇——动静态库(动静态库的介绍+动静态库的打包与使用)

  1. 上面这两个步骤其实就把静态库打包好了,下面我们还有做一个工作就是发布静态库,简单地说,就是把头文件和静态库组织起来,头文件放在include 下,如下:
    在这里插入图片描述
    这样一个库文件就可以给别人使用了。
    上面的所有步骤我们可以写进Makefile里,利用make指令一键打包和make output发布,如下:
libmymath.a:myadd.o mysub.oar -rc $@ $^myadd.o:myadd.cgcc -c $<mysub.o:mysub.cgcc -c $<#清理.PHONY:cleanclean:rm -rf output *.o *.a#发布.PHONY:outputoutput:mkdir -p myliba/includemkdir -p myliba/libcp *.h myliba/include/cp *.a myliba/lib/

使用Makefile打包和发布:
在这里插入图片描述

🌏静态库的使用

先把静态库放到一个测试目录下:
【Linux篇】第十篇——动静态库(动静态库的介绍+动静态库的打包与使用)

然后编写一段代码:

#include #include "myadd.h"#include "mysub.h"int main(){  int a = 20, b = 10;  printf("%d+%d=%d\n", a, b, MyAdd(a, b));  printf("%d-%d=%d\n", a, b, MySub(a, b));  return 0;}

编写Makefile:
使用gcc编译时,采用静态链接编译,所以要带上选项**-static**,此外,因为我们使用了别人给的静态库,所以我们还有告诉编译器库文件所在路径,头文件所在路径以及库名,所以要用到以下三个选项:

  • -L: 指明库文件所在路径
  • -I: 指明头文件所在路径
  • -l: 指明库文件名称,这里库名就是mymath(去掉前缀lib和后缀.a)

这里我们可以使用绝对路径,使用下面的shell命令获取当前所在路径:

path=$(shell pwd)

Makefile编写后如下:

path=$(shell pwd)mytest:mytest.c#-l 指定库目录名称 -L 库目录路径 -I 指定头文件路径gcc -o $@ $^ -I $(path)/mylib.a/include -L $(path)/mylib.a/lib -l mymath -lm -static .PHONY:cleanclean:rm -r mytest

开始编译程序:
【Linux篇】第十篇——动静态库(动静态库的介绍+动静态库的打包与使用)
执行程序:
【Linux篇】第十篇——动静态库(动静态库的介绍+动静态库的打包与使用)
使用file指令查看链接属性:
【Linux篇】第十篇——动静态库(动静态库的介绍+动静态库的打包与使用)

🌏动态库的打包和使用

🌲动态库的打包

我们同样还是使用上面的那四个文件进行演示。
步骤:

  1. 先将myadd.cmysub.c 变成生成对应的二进制文件。注意这里生成的二进制文件要带上选项**-fPIC**,产生路径无关码,也就是这里使用相对地址,是动态确定的,不存在绝对地址
gcc -fPIC -c myadd.c mysub.c
  1. 使用gcc带上选项**-shared** (生成共享的库格式)对二进制文件进行打包
gcc -shared -o libmymath.so myadd.o mysub.o
  1. 最后一步就是对动态库进行发布,也就是将库文件和头文件进行组织打包

编写akefile:

libmymath.so:myadd.o mysub.ogcc -shared -o $@ $^myadd.o:myadd.cgcc -c $<mysub.o:mysub.cgcc -c $<#清理.PHONY:cleanclean:rm -rf output *.o *.so#发布.PHONY:outputoutput:mkdir -p mylibso/includemkdir -p mylibso/libcp *.h mylibso/include/cp *.so mylibso/lib/

使用Makefile打包和发布:
在这里插入图片描述

🌲动态库的使用

先把动态库放到测试目录下:
【Linux篇】第十篇——动静态库(动静态库的介绍+动静态库的打包与使用)
然后编写Makefile,和静态库的使用类似:

path=$(shell pwd)mytest:mytest.c#-l 指定库目录名称 -L 库目录路径 -I 指定头文件路径gcc -o $@ $^ -I $(path)/myliba/include -L $(path)/myliba/lib -l mymath -lm -static .PHONY:cleanclean:rm -r mytest

编译程序: 如果此时直接对程序进行编译,不会报错,但是执行的时候会报错,无法打开共享库里面的文件
【Linux篇】第十篇——动静态库(动静态库的介绍+动静态库的打包与使用)
因为这里是动态链接,不仅要让编译器动态库的路径,还要让操作系统知道,所以这里我们需要导入一个环境变量LD_LIBRARY_PATH,如下:

export LD_LIBRARY_PATH=/home/wxj/code/2022_linux/linuxcode/test_4_2/lib/test/mylibso/lib

此时再执行程序就不会报错了:
【Linux篇】第十篇——动静态库(动静态库的介绍+动静态库的打包与使用)
使用file指令查看程序的链接属性:
【Linux篇】第十篇——动静态库(动静态库的介绍+动静态库的打包与使用)
使用ldd指令查看程序依赖的库:
【Linux篇】第十篇——动静态库(动静态库的介绍+动静态库的打包与使用)

🌐总结

以上就是动静态库的全部内容。大家也可以去尝试打包一下动静态库,这样可以更深入地了解其中的细节。喜欢的话,欢迎点赞支持和关注~
在这里插入图片描述