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

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

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

正文开始@呀小边同学

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

ldd [某可执行程序]  

于是我们查看一下可执行程序mytest依赖的库,诶,在茫茫人海中,发现了C标准库 ——

在这里插入图片描述

查看发现,这是一个软链接,它最终指向的是libc-2.17.so

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

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

(其中.cc/.cpp/.cxx都可作为C++文件的后缀,当然了,常用的还是前两个,被见怪就行了)

1. 动态库 & 静态库

💛 旧知复习

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

  • 动态库:库文件以.so为后缀;

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

库文件的命名规则:libname.so 或者 libname.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 & 动态链接版本

本文完

  • 21
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 22
    评论
评论 22
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

浮光 掠影

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

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

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

打赏作者

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

抵扣说明:

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

余额充值