当前位置: 首页 > article >正文

Linux练级宝典->动态库和静态库

动静态库的原理

我们知道可执行文件前的4步骤 预编译->编译->汇编->链接

  • 预处理: 完成头文件展开、去注释、宏替换、条件编译等,最终形成xxx.i文件。
  • 编译: 完成词法分析、语法分析、语义分析、符号汇总等,检查无误后将代码翻译成汇编指令,最终形成xxx.s文件。
  • 汇编: 将汇编指令转换成二进制指令,最终形成xxx.o文件。
  • 链接: 将生成的各个xxx.o文件进行链接,最终形成可执行程序。

例如,我们上面用5个文件组合出了可执行文件。.0文件就是汇编后的文件。

 我们在最后一步的时候不链接,而是把这些.o文件打包,

这样的一堆.o文件组合到一起就是一个库了。 

认识动静态库

#include <stdio.h>

int main()
{
	printf("hello world\n"); //库函数
	return 0;
}

上述代码我们用了一个库函数printf。

生成后,我们用ldd查看一个可执行程序依赖的库文件。

libc.so.6就是依赖的文件。

我们找到上面的文件目录,我们看到我们上面的连接其实就是一个软连接。 

我们可以查查看这个库文件

我们可以看到他是一个shared object。

 shared object说明这就是一个共享的目标文件库,准确来说,还是一个动态库

  • linux中 以.so为后缀的是动态库,以.a为后缀的是静态库
  • Windows中,以.dll为后缀的是动态库,以.lib为后缀的是静态库。

这里可执行程序锁依赖的.so文件就是动态库,gcc/g++默认的是动态链接,若想进行静态链接,可以加一个-static选项。

gcc -o mytest-s mytest.c -static

可以看到我们静态库 的大小远远大于可执行文件。

我们查看二者的情况,一个是dynamically 动态链接 一个是 statically 静态链接。

 动静态库各自的特征

静态库

静态库是在编译链接时期,直接把库的代码复制一份到可执行文件中,生成的可执行程序在运行的时候不再需要静态库,所以里面多了一个库的代码所以肯定会大。 

优点:

静态库,只要生成可执行程序后,这个程序就可以独立运行了,静态库就没用了

缺点:

由上面也可以看出静态库生成的代码会占用大量的空间,并且如果这个库被多个程序使用,就会导致每个程序都多一个静态库代码。代码还都是重复的

动态库

 动态库:就是在程序运行时,在共享区中找到对应的动态库,此时去动态库中调用代码。

动态库就相当于,一开始就在物理内存加载好一份,然后程序调用对应代码时,通过虚拟内存和物理内存映射的方式,找到对应的函数。

优点:

节省空间,多个库调用的都是在物理内存中的同一个库,即同一个代码。

缺点:

必须依赖动态库,否则无法运行。

静态库的打包与使用

 我们写4个文件,add.h和add.c和sub.h和sub.c都是很简单的代码

就是一个头文件,一个原文件,里面就一个加减函数。头文件声明这个函数。

1.生成.o文件。

 生成两个.o文件 gcc -c选项。

2.使用ar命令将所有目标文件打包为静态库

ar指令常用于将目标文件打包为静态库

-r: 若静态库文件中的目标文件有更新,则用新的目标文件替换为旧的目标文件(replace)

-c:建立静态库文件(create)

ar -rc libcal.a add.o sub.o

 -t:列出静态库的文件

-v:显示详细信息。

 3.把头文件和生成的静态库组织起来

当我们把自己的库给别人用的时候,可以看到上面我们有一个静态库文件和剩下的.h的文件,所以我们就是要把头文件和.o的静态库文件给到别人就可以。

makefile文件:

mylib=libcal.a
CC=gcc

$(mylib) : add.o sub.o
	ar -rc -o $(mylib) $^
%.o:%.c
	$(CC) -c $<

.PHONY:clean
clean:
	rm -f $(mylib) ./*.o

.PHONY:output
output:
	mkdir -p mathlib/include
	mkdir -p mathlib/lib
	cp ./*.h mathlib/include
	cp ./*.a mathlib/lib

 make编译文件,makeout将文件放入mathlib文件夹中。

此时就是把mathlib给到别人,此时别人有静态库和头文件.h,只差最后一步链接即可使用。

使用静态库

创建一个源文件main.c,然后使用库函数。

#include <stdio.h>
#include <add.h>

int main()
{
	int x = 20;
	int y = 10;
	int z = my_add(x, y);
	printf("%d + %d = %d\n", x, y, z);
	return 0;
}

此时我们的文件夹里只有两个文件,main.c和我们打包的静态库

 方法一:使用选项

 此时使用gcc编译main.c文件生成可执行程序就需要携带三个选项了

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

指定库代码如下:

gcc main.c -I ./mathlib/include/ -L ./mathlib/lib/ -lcal

 结果如下:

 为什么要指定搜索文件?

我们知道操作系统中有环境变量这个说法,所以我们在使用一些系统的库的时候,没有指定路径,而是直接使用,而我们自己定义的库没在环境变量下,此时使用就得带上指定的路径,让系统去找。当然也可以把这个路径放到环境变量下。

1.指定头文件的路径,是找到头文件->找到声明。

2.指定库的路径,是找到库->找到定义。

3.因为我们库可能不只是一个,所以我们要指定需要找到哪个库文件,所以-lcal就是指定名字。

4.上述三个选项后可以加空格也可以不加。

方法二:把头文件和库文件塞到对应的系统路径下

 就是进入环境变量的意思,放到系统路径后就能让操作系统自动找到对应库了。

将库文件拷贝进系统路径下。 

sudo cp mathlib/lib/libcal.a /lib64/
sudo cp mathlib/include/* /usr/include/

 结果如下:

我们发现我们把文件放到系统路径后,只用指定库的名字就完成了调用。 

实际我们把头文件和库文件塞到系统路径就是安装库的过程,但不推荐将自己写的头文件和库文件拷贝到系统路径下,这样其实是造成污染的。

动态库的打包与使用

打包

动态库和静态库都是库,但是二者在打包时有一点点区别。我们还是使用上述的4个文件进行打包

 1.还是让源文件生成.o文件

这里生成文件时,需要多加一步操作:

带上选项 -fPIC(position independent code):产生位置无关码。

gcc -fPIC -c add.c sub.c

选项说明:

-c 生成.o文件。

-fPIC :生成位置无关码。告诉编译器产生位置无关码,给动态库使用

2.使用-shared选项将所有目标文件打包为动态库

和静态库ar 指令不同,动态库的生成用 gcc 带 -shared选项。 

gcc -shared -o libcal.so add.o sub.o

我们发现此时生成的动态库的大小 << 静态库的大小。 

3.将头文件和生成的静态库组织到一个文件夹下面

 使用makefile把文件组织起来

原始步骤就是

我们用makefile将指令操作汇总一下:

mylib=libcal.so

%.o:%.c
	gcc -fPIC -c $<
$(mylib):add.o sub.o
	gcc -shared -o $(mylib) $^


.PHONY:clean
clean:
	rm -rf $(mylib) ./*.o

.PHONY:output
output:
	mkdir -p mlib/include
	mkdir -p mlib/lib
	cp ./*.h mlib/include
	cp ./*.so mlib/lib

注意:makefile的缩进要用tab键,用空格键可能会出错。

 结果如下:

 此时继续使用main.c测试整个库代码。

#include <stdio.h>
#include <add.h>

int main()
{
	int x = 20;
	int y = 10;
	int z = my_add(x, y);
	printf("%d + %d = %d\n", x, y, z);
	return 0;
}
 gcc main.c -I./mlib/include -L./mlib/lib -lcal

使用指令后,生成可执行文件。

我们发现文件不能执行。

我们发现我们的动态库文件是not found的。

方法一:拷贝.so文件到系统共享路径下

 这里就是让你把.so拷贝到和 c库文件同一个目录下,此时操作系统就会自动找到库了。

sudo cp ./libcal.so /lib/x86_64-linux-gnu/

 方法二:更改LD_LIBRARY_PATH

 LD_LIBRARY_PATH是一个环境变量,这个环境变量是程序运行时要搜索的路径。所以把动态库所在的目录添加到LD_LIBRARY_PATH即可。

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/~/blog-code/DSL-D

 

我们看到.so上面也有一串地址,说明当前库是找到了。

 方法三:配置/etc/ld.so.conf.d/

这个目录下面放的都是以.conf为后缀的配置文件,这些配置文件就是一堆路径,和环境变量一样,系统会自动进行查找。

echo ~/blog-code/DSL-D/mlib/lib > aron.conf
sudo cp aron.conf /etc/ld.so.conf.d/

上述两个命令:第一个是将上面的地址打印到aron.conf中

第二个是把aron.conf放到上述目录中。

sudo ldconfig

使用上述指令刷新配置文件。

此时执行文件


http://www.kler.cn/a/587570.html

相关文章:

  • Vue 3 vs Vue 2:深入解析从性能优化到源码层面的进化
  • 深入React Redux:原理剖析与高效实践指南
  • Sequelize:Node.js 项目中数据库管理的 “秘密武器”
  • 洛谷 P2801 教主的魔法 题解
  • Mac 上编译 Ragflow
  • 《灵珠觉醒:从零到算法金仙的C++修炼》卷三·天劫试炼(39)玲珑棋局摆硬币 - 零钱兑换(完全背包)
  • python列表基础知识
  • 3.4 Spring Boot整合Elasticsearch:全文检索与聚合分析
  • 信奥赛CSP-J复赛集训(模拟算法专题)(16):P6386 [COCI 2007/2008 #4] VAUVAU
  • Linux下对2TB磁盘的分区、格式化、挂截目录介绍
  • 用Python和Pygame实现打砖块游戏
  • HTML编辑MP4保存名称
  • DBeaver部分操作指南(数据库连接,构造ERD图,格式化SQL)
  • Hive函数大全:从核心内置函数到自定义UDF实战指南(附详细案例与总结)
  • 无电池也能通信!中国移动5G-A芯片重塑物联网未来
  • P2512糖果传递 P4447分组 P1080国王游戏 P4053建筑抢修
  • Python 字节码深度历险:dis 模块揭秘与性能优化实战
  • 深入探讨RAID 5的性能与容错能力:实验与分析(磁盘阵列)
  • Vue 中的 MVVM、MVC 和 MVP 模式深度解析
  • Java数据结构第二十三期:Map与Set的高效应用之道(二)