0


【Linux】动静态库@基础IO —— 动静态库的制作使用

制作动静态库

我们其实一直都在直接或间接的使用库,本文将介绍动静态库的制作和使用。

从今天开始,你的朋友说,诶?你的作业借我看看。你就可以,哦不你也应该,勇敢的做个高尚的人,制作一个库扔给他~

正文开始@呀小边同学

💛 显示可执行程序依赖的库。或者说查看依赖的动态库,因为静态链接会把这个模块拷贝到可执行文件中了,也就没有依赖而言了

ldd [某可执行程序]

于是我们查看一下可执行程序

mytest

依赖的库,诶,在茫茫人海中,发现了C标准库 ——

在这里插入图片描述

查看发现,这是一个软链接,它最终指向的是

libc-2.17.so

这就是我们使用的C标准库,它的大小是 2156592 这么些。我们使用的标准IO、字符串操作和整数数学函数所谓的这些的库,也不过是安装在系统中的文件罢了。

那我们再来看看C++的库?

stdc++

(其中

.cc

/

.cpp

/

.cxx

都可作为C++文件的后缀,当然了,常用的还是前两个,被见怪就行了)

1. 动态库 & 静态库

💛 旧知复习

在Linux中,一般库分为两种:动态库和静态库,它们就是文件!

  • 动态库:库文件以.so为后缀;
  • 静态库:库文件以.a为后缀(archieve 一种称为存档的特殊文件格式)。

库文件的命名规则:lib

name

.so 或者 lib

name

.a [.后面可能跟其他的内容]

库的真实名字掐头去尾就是库名称。即去掉lib前缀并去掉.so/.a后缀,如上文中的

c

库。

(在windows中,动态库以.lib为后缀,静态库以.dll为后缀)

从前从前,我们就说过,gcc默认是动态链接编译,静态编译需要带选项

-static

在这里插入图片描述

观察到动态链接时文件体积较小,静态链接时文件体积较大。这是因为静态库是在链接时,会复制程序引用的目标模块到可执行程序中。而所谓共享动态库,实际上是通过虚拟地址空间映射到同一位置,这样生成的可执行文件体积小,但是可移植性差。

后悔CSAPP没好好学(

我现在用到的服务器,没有内置语言的静态库,而只有动态库,需要yum安装C/C++静态库。

sudo yum install glibc-static
sudo yum install libstdc++-static

这都是小事儿。

另外,我们很多Linux命令是用C语言写的,而且是动态链接的 ——

所以如果你把C语言库干掉了,那你的系统就寄了,当然了如果你是云服务器,不拍折腾的宝子可以试试。。

💛 下面即将介绍动静态库的制作和使用

我们可以打开刚刚的C标准库,发现就是人类看不懂的二进制文件 ——

那我们如何得知,这个库给我们提供了哪些方法?

实际上一套完整的库是要包括三样东西:库文件本身头文件、说明文档。

我们在写C/C++代码时,有时候将头文件和源文件分离:在

.h

中放入声明,在

.c

.cpp

放入实现。为什么要这样设计?

原因之一就是我们要制作库,这样方便使用,且私密。其中头文件是人类能理解的文本,会暴露出库中方法的基本使用。

这些头文件被安装在

usr/include/

目录下,可以看到一些熟悉的头文件:

随意打开其中的 stdio.h,哦豁~

2. 制作静态库

💛 准备工作:

(把我们刚刚一堆的测试文件挪到other目录下)

我们想在

mytest.c

这个文件中使用这个“库”,但是并不能编译通过。

报错是gcc不能找到库的源文件,因此我们在编译时要跟上源文件位置 ——

gcc user.c test_lib/add.c test_lib/sub.c

yeah~

但我们不想止于此,这还远不能称为制作库。

众所周知,程序的编译运行经历了如下过程:

如果想把我们自己的方法给别人用,你固然可以提供源代码和头文件,但是我们不想把源文件给别人——于是将所有的

.o

打包成库,照样可以链接成功。制作库的本质就是,**把一堆

.o

文件以某种方式打包**。

下面来讨论具体细节。

2.1 制作

我们会在test_lib目录下写这样一个

Makefile

文件 ——

可能你看着眼晕,但其实特简单!

🔸 1. 将库文件全部编译

.o

🔸 2. 再用

ar

命令,把所有的

.o

打包在一起

ar -rc libname.a [待打包.o]
  • ar是gnu的归档工具,相当于打包成指定名称的文件
  • rc表示replace and create

🔸 3. 可以make output 发布

​ 要把

.h

文件也给别人哦~ 这样人类才知道你库里面有什么

**从今天开始,你的朋友说,小边啊!你的代码借我看看。你,你就应该勇敢的做个高尚的人,制作一个库

output

丢给他~**

Makefile文件贴给宝子们,你最好,哦不,你也应该自己闭上眼睛来写 ——

lib_mymath.a:add.o sub.o    
  ar -rc lib_mymath.a $^%.o:%.c    
  gcc -c $<.PHONY:output    
output:    
  mkdir output     
  cp *.h output     
  cp lib_mymath.a output
      
.PHONY:clean    
clean:    
  rm -rf *.o lib_mymath.a output      
  • 其中%表示通配符
  • $<表示依次形成对应同名文件

查看静态库

ar -tv lib_mymath.a
  • t:列出静态库中的目录列表
  • v:verbose 详细信息

当然你也可以查看C标准库,我们安装好的C静态库,就是把所有的源文编译为

.o

打包给我们的!我靠!C语言中这么多库函数呀~

你甚至可以把库安装到系统中,当然了,我们也不建议这样做~

.PHONY:install
install:
    cp *.h /usr/include       #头文件
    cp lib_mymath.a /lib64  #静态库

2.2 使用

我把库丢给了我的最佳损友,他还跟我说不会用~ 我害得给他写一个教程,我真的,我哭死…

我损友所在目录是

friend

,我帮他把

output

这个文件**[库文件+一套头文件]**拷贝到他的目录下顺便把名字也改成

lib

,命令如下

 cp -rf test_lib/output/ friend/lib        #宝子们根据自己情况弄哈

🔹 直接编译会报错:找不到头文件。那当然找不到,编译器并不会查找你同级目录下目录中有啥, 因此要带

-I./lib

,指明在当前目录下的lib目录下找;

🔹 然后呢,又报错了:这又找不到库函数的实现了,因此

-L./lib

,要告知库路径在哪儿;

🔹 但是实际情况可能有很多库,编译器也不知道链接这个路径下哪个库,因此同时要带上库名称

-l lib_mymath.a

(空格可有可无),注意掐头去尾才是库的名字。

  • -I:指明头文件搜索路径
  • -L:指明库文件搜索路径
  • -l:指明要链接哪个库

**算了算了,我再写个

Makefile

文件扔给他吧~**

homework:homework.c    
  gcc -o $@ $^-I./lib -L./lib -l_mymath                                                                                                      
.PHONY:clean                             
clean:                                   
  rm -rf homework

让人疑惑的是,但是我们之前写C/C++代码,也用了各种库,为什么就不需要这些选项来指明些什么呢?

因为之前的库默认在系统的路径下:编译器是能识别这些存在于配置文件中的路径的

  • 库文件:/lib64, /usr/lib
  • 头文件:/user/include等

上面的过程,也就是一般软件的安装过程。

也就是说,如果我想偷懒,完全可以把对应的库和头文件拷贝到默认路径下,但是我们严重不推荐!宝子!(如果你命名不标准,或与库中冲突,可能直接覆盖了,这事儿咱也不是没干过。。)

3. 制作动态库

3.1 制作

🔸 1. 还是要将库文件全部编译

.o

,只不过要带选项

-fPIC

,形成与位置无关码position independent code

gcc -fPIC -c add.c sub.c

什么叫与位置无关呢?我们可以简单地理解为,库文件可以在内存中的任意位置加载,且不影响和其他程序的关联性,这不是本文的重点。事实上我也说不明白,后悔CSAPP学时候没通读(

🔸 2. 要把库打包,我们不再使用

ar

命令,记得带选项

-shared
#形成一个动态链接的共享库
gcc -shared -o $@ $^

🔸 3. 可以make shared_lib发布一下:其中包含

*.h

.so

哦~

#形成一个动态链接的共享库
lib_mymath.so:add.o sub.o    
  gcc -shared -o $@ $^                                                                                                                        
#产生.o文件,程序内部的地址方案与位置无关    
%.o:%.c    
  gcc -fPIC -c $<.PHONY:shared_lib    
shared_lib:    
  mkdir shared_lib     
  cp *.h shared_lib     
  cp lib_mymath.so shared_lib
      
.PHONY:clean    
clean:    
  rm -rf *.o lib_mymath.so

3.2 使用

把我的库文件拷贝给我损友,害,我再给他写一个

Makefile

吧~

 cp -rf test_lib/shared_lib/ friend/ #宝子们根据自己情况操作哦

🔹 同样的我们需要指定路径搜索头文件

-I./lib

同样的也需要指明库文件的搜索路径

-L./lib

同样的也要指明链接哪个库 ——

// Makefile
homework:homework.c    
  gcc -o $@ $^-I./shared_lib -L./shared_lib -l_mymath                                                                                       
.PHONY:clean    
clean:    
  rm -f homework 

这些看起来和静态库的使用都没什么区别,编译也能通过 ——

但是一**运行

./homework

就报错了。为什么啊?静态库也没这回事啊?因为静态库把目标模块直接拷贝进去,运行时不需要再找;而动态库,编译时需要找,运行时也需要加载动态库**。

可是我编译时不是已经告诉了库路径了吗?但这只是告知了编译器头文件库路径在哪里,当程序编译完成后,已与编译器无关,运行的时候加载器还是不知道它们在哪儿。

🔹 于是 —— 咱们需要在运行时进一步告知系统库在哪,有这样两种常见做法:

  1. 动态库头文件拷到共享库路径/usr/bin下,强烈不建议
  2. 通过导入LD_LIBRARY_PATH这个环境变量,指明程序启动后动态库的搜索路径。这是最推荐的方式。不过它一般是没有设置的 ——确实喂,我这儿只有一个之前下载VimForCpp配置的路径。export LD_LIBRARY_PATH=路径 #导入环境变量当然了,这种在命令行设置的环境变量,只在本次会话有效,退出登陆后再进来就没了。如果想让环境变量永久生效,可以把它添加到登陆的启动脚本里你可以vim ~/.bash_profile或者vim ~/.bashrc。但是,不建议改 看到了没?你下载VimForCpp时候也是添加了的
  3. 配置系统文件/etc/ld.so.conf.d/,是系统搜索动态库的路径,这种做法可以永久生效。在这里我们打开任意一个看看,其中也不过是一个配置路径我们需要以超级用户的身份su - 进入:①添加配置文件,②在其中添加库的搜索路径,③并更新缓存。[root@VM-24-5-centos ld.so.conf.d]# touch oneManBand.conf#一顿cd, 拿到库路径[root@VM-24-5-centos shared_lib]# pwd/home/bts/linux-code/link/friend/shared_lib[root@VM-24-5-centos ld.so.conf.d]# vim oneManBand.conf /home/bts/linux-code/link/friend/shared_lib #添加库的搜索路径~~执行如下命令,更新库路径缓存ldconfig #更新库路径缓存从此开始,永久生效了。在这里插入图片描述但是我还是不建议你这样做,如果你把这配置文件删了,那当然又不行了(我还是把它干掉吧~ 别忘了以root身份执行哦[root@VM-24-5-centos ld.so.conf.d]# rm oneManBand.conf[root@VM-24-5-centos ld.so.conf.d]# ldconfig

推荐宝子们前期使用

LD_LIBRARY_PATH

这样导入,这样看起来经常让我们变得很麻烦的做法是为了让你更熟悉。

4. 总结

其实今天一共就干了两件事儿,制作和使用 ——

💛 如何制作:

  1. 所有的源代码,都需要先被编译成为.o(可重定位目标文件)
  2. 制作动/静态库的本质,就是将所有的.o “打包” (ar或gcc),这样方便传送- 静态库:ar -rc- 动态库:gcc -fPIC -shared
  3. 交付:#include *.h + .a.so文件

💛 如何使用:拿着别人的库和头文件,加入到自己的项目中

静态库直接可用,动态库还要告知系统动态库搜索路径。

如果你提供的是一个静态库,那我们只能将这个库静态链接到程序中;如果你提供的是一个动态库,那我们只能将这个库动态链接到程序中。如果既想动态链接,又想静态链接,一般要提供两个版本的库文件。

那gcc/g++优先链接哪一个呢?一定是优先动态的。从前从前我们就说过,默认是release & 动态链接版本

本文完

标签: linux 服务器 运维

本文转载自: https://blog.csdn.net/qq_54851255/article/details/125807460
版权归原作者 呀小边同学 所有, 如有侵权,请联系我们删除。

“【Linux】动静态库@基础IO &mdash;&mdash; 动静态库的制作使用”的评论:

还没有评论