Linux动静态库

简介: Linux动静态库

一、了解动静态库

一堆源文件和头文件最终变成一个可执行程序需要经历以下四个步骤:


预处理: 完成头文件展开、去注释、宏替换、条件编译等,最终形成xxx.i文件

编译: 完成词法分析、语法分析、语义分析、符号汇总等,检查无误后将代码翻译成汇编指令,最终形成xxx.s文件

汇编: 将汇编指令转换成二进制指令,最终形成xxx.o文件

链接: 将生成的各个xxx.o文件进行链接,最终形成可执行程序

实际上,所有库本质是一些目标文件(xxx.o)的集合,库的文件当中并不包含主函数而只是包含了大量的方法以供调用,可以认为动静态库本质是可执行程序的"半成品"


在Linux当中,以.so为后缀的是动态库,以.a为后缀的是静态库

在Windows当中,以.dll为后缀的是动态库,以.lib为后缀的是静态库

下面通过一份简单的代码来具体认识一下动静态库

#include <stdio.h>
int main()                                                                                                                                                                       
{
    printf("hello lib\n");//printf()为库函数
    return 0;
}

通过ldd命令可以查看可执行程序所依赖的库文件


023d3eb653044861822543cebbb6c79c.png


从上图可以看出test可执行程序依赖 /lib64/libc.so.6 ,但是其是一个软链接,链接的源文件为/lib64/libc-2.17.so。这个 libc-2.17.so 实际上就是C动态库,去掉一个库前缀lib,再去掉后缀,剩下的就是库的名字。


而gcc/g++默认进行的是动态链接,想使用静态链接需添加 -static 选项,且静态链接生成的可执行程序并不依赖其他库文件。


31287139672c4dbab2edff033d395369.png


而且 使用静态库的可执行程序的大小 明显大于 使用动态库的可执行程序的大小


3a1549b2760042e793081b03c3d26c67.png


1.1 静态库的特点

静态库是程序在编译链接的时候把库的代码复制到可执行文件当中的,生成的可执行程序在运行的时候将不再需要静态库,因此使用静态库生成的可执行程序的大小一般比较大


优点:使用静态库生成可执行程序后,该可执行程序可独自运行,不再依赖库了

缺点:使用静态库生成可执行程序会占用大量空间,特别是当有多个静态程序同时加载而这些静态程序使用的都是相同的库,这时在内存当中就会存在大量的重复代码

1.2 动态库的特点

动态库是程序在运行的时候才去链接相应的动态库代码的,不必将库的代码复制到可执行文件当中,使得可执行程序的大小较使用静态库而言更小,节省磁盘空间。一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码。


在可执行文件开始运行前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中,这个过程称为动态链接。操作系统采用虚拟内存机制使得物理内存中的一份动态库被所有要使用该库的进程共用,节省了内存空间。

bf601e3666f7488196e3d0d211c31ef0.png



优点:节省内存和磁盘空间,当多个用到相同动态库的程序同时运行时,库文件会通过进程地址空间进行共享,内存当中不会存在重复代码

缺点:必须依赖动态库,否则无法运行

二、静态库的打包与使用

下面演示时,都以这些文件为例。源文件main.c、test1.c和test2.c,头文件test1.h和test2.h


main.c文件

#include "test1.h"
#include "test2.h"
int main()
{
    test1();
    test2();                                                                                                                                                                     
    return 0;
}

test1.h文件

#include "test1.h"                                                                                                                                                               
void test1(){
    printf("This is test1!!!\n");
}

         

test2.c文件

#include "test2.h"
void test2(){
    printf("This is test2!!!\n");                                                                                                                                                
}

test2.h文件

#pragma once                                                                                                                                                                     
#include <stdio.h>
void test2();

2.1 静态库的打包

第一步:将要打包的源文件生成对应的目标文件

test1.o:test1.c
    gcc -c tets1.c -o test1.o
test2.o:test2.c
    gcc -c test2.c -o test2.o

第二步:使用ar命令将所有目标文件打包为静态库


ar命令是gnu的归档工具。下面使用ar命令的-r和-c选项进行打包


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

(create):建立静态库文件

libtest.a:test1.o test2.o
    ar -rc libtest.a test1.o test2.o

此外,我们可以用ar命令的- t 和 -v 选项查看静态库当中的文件。


-t:列出静态库中的文件

-v(verbose):显示详细的信息

f459119e8db7480886219c67b40b8fe6.png


第三步:将头文件和生成的静态库组织

mkdir -p my_lib/include
mkdir -p my_lib/lib
cp ./*.h ./my_lib/include 
cp ./*.a ./my_lib/lib

当想发布库给别人使用时,发布这个文件夹即可


makefile完整版

libtest.a:test1.o test2.o
    ar -rc $@ $^
test1.o:test1.c
    gcc -c $^ -o $@
test2.o:test2.c
    gcc -c $^ -o $@
.PHONY:output
output:
    mkdir -p my_lib/include
    mkdir -p my_lib/lib
    cp ./*.h ./my_lib/include 
    cp ./*.a ./my_lib/lib
.PHONY:clean
clean:
    rm -rf ./my_lib ./*.o ./*.a           

2.2 静态库的使用

方案一:使用选项


-I:指定头文件搜索路径

-L:指定库文件搜索路径

-l:指明需要链接库文件路径下的具体哪一个库

e30e20d2dbe24c4c85e7287da80d023c.png


方案二:将头文件和库文件拷贝到系统路径下

[bjy@VM-8-2-centos usedir]$ sudo cp output/include/* /usr/include/
[bjy@VM-8-2-centos usedir]$ sudo cp output/lib/libtest.a /lib64/

注意:虽已将头文件和库文件拷贝到系统路径下,但使用gcc生成可执行程序时,还需具体指明链接哪一个库

31d5e570f30a4027bcea1dc1a3030d42.png



实际上拷贝头文件和库文件到系统路径下的过程,就是安装库的过程。但并不推荐将个人编写的头文件和库文件拷贝到系统路径下,会对系统文件造成污染。


三、动态库的打包与使用

3.1 动态库的打包

第一步:将要打包的源文件生成对应的目标文件


此时需要添加 -fPIC 选项 ,即产生位置无关码

test1.o:test1.c
    gcc -fPIC -c test1.c -o test1.o
test2.o:test2.c
    gcc -fPIC -c test2.c -o test2.o

-fPIC作用于编译阶段,告诉编译器产生与位置无关的代码,此时产生的代码中没有绝对地址,全部都使用相对地址,从而代码可以被加载器加载到内存的任意位置都可以正确的执行(通过页表与共享区建立映射关系)。所以共享库被加载时,在内存的位置不是固定的 。


第二步:使用-shared选项将所有目标文件打包为动态库

libtest.so:test1.o test2.o
    gcc -shared $^ -o $@

第三步:组织头文件与生成的动态库

mkdir -p my_lib/include
mkdir -p my_lib/lib
cp ./*.h ./my_lib/include 
cp ./*.so ./my_lib/lib        

   

makefile完整版

libtest.so:test1.o test2.o
    gcc -shared $^ -o $@
test1.o:test1.c
    gcc -fPIC -c test1.c -o test1.o
test2.o:test2.c
    gcc -fPIC -c test2.c -o test2.o
.PHONY:output
output:
    mkdir -p my_lib/include
    mkdir -p my_lib/lib
    cp ./*.h ./my_lib/include 
    cp ./*.so ./my_lib/lib                                                                                                                                                       
.PHONY:clean
clean:
    rm -rf *.so *.o my_lib

3.2 动态库的使用

接下来使用与静态库相同的方式,用-I选项指定头文件搜索路径,用-L选项指定库文件搜索路径,最后用-l选项指明库的名称。但是可以发现可执行程序貌似找不到这个动态库的位置。

9fd813423283488c9fc19fd0f356985e.png



因为上面的三个选项是传给gcc编译器的,但可执行程序生成后就与编译器无关了,所以当程序运行起来后操作系统并找不到所依赖的动态库。


解决方案一:拷贝.so文件到系统共享库路径下

[bjy@VM-8-2-centos uselib]$ sudo cp ./my_lib/lib/libtest.so /lib64

c8c0e38ef1d5423984d1b3f9facf92df.png

解决方案二:更改LD_LIBRARY_PATH


LD_LIBRARY_PATH是程序运行动态查找库时所要搜索的路径,只需将动态库所在的目录路径添加到LD_LIBRARY_PATH环境变量当中即可

380fd70e3dd0458590744a7881b4d475.png

但是这个方案只能临时解决问题,当将终端关闭再重新开启后,导入的环境变量就会丢失。

解决方案三:配置/etc/ld.so.conf.d/


/etc/ld.so.conf.d/路径下存放的都是以.conf为后缀的配置文件,而这些配置文件当中存放的都是路径,系统会自动在/etc/ld.so.conf.d/路径下找所有配置文件里面的路径,然后就在每个路径下查找用户所需要的库。若将第三方库文件的路径也放到该路径下,那么当可执行程序运行时,系统就能找到第三方库文件。

b22867c2c7764cd0b4c0fa2e80b667c4.png

先将库文件所在目录的路径存入一个以.conf为后缀的文件当中,再将该文件拷贝到/etc/ld.so.conf.d/目录下,最后使用 ldconfig 命令将配置更新一下即可。


相关实践学习
CentOS 7迁移Anolis OS 7
龙蜥操作系统Anolis OS的体验。Anolis OS 7生态上和依赖管理上保持跟CentOS 7.x兼容,一键式迁移脚本centos2anolis.py。本文为您介绍如何通过AOMS迁移工具实现CentOS 7.x到Anolis OS 7的迁移。
目录
相关文章
|
1月前
|
存储 编译器 Linux
动态链接的魔法:Linux下动态链接库机制探讨
本文将深入探讨Linux系统中的动态链接库机制,这其中包括但不限于全局符号介入、延迟绑定以及地址无关代码等内容。
641 23
|
5月前
|
安全 Linux vr&ar
Linux的动态库和静态库
Linux的动态库和静态库
|
3月前
|
Linux API 开发工具
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
ijkplayer是由B站研发的移动端播放器,基于FFmpeg 3.4,支持Android和iOS。其源码托管于GitHub,截至2024年9月15日,获得了3.24万星标和0.81万分支,尽管已停止更新6年。本文档介绍了如何在Linux环境下编译ijkplayer的so库,以便在较新的开发环境中使用。首先需安装编译工具并调整/tmp分区大小,接着下载并安装Android SDK和NDK,最后下载ijkplayer源码并编译。详细步骤包括环境准备、工具安装及库编译等。更多FFmpeg开发知识可参考相关书籍。
132 0
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
|
5月前
|
Linux API
在Linux中,程序产生了库日志虽然删除了,但磁盘空间未更新是什么原因?
在Linux中,程序产生了库日志虽然删除了,但磁盘空间未更新是什么原因?
|
6月前
|
Oracle 关系型数据库 Linux
讲解linux下的Qt如何编译oracle的驱动库libqsqloci.so
通过这一连串的步骤,可以专业且有效地在Linux下为Qt编译Oracle驱动库 `libqsqloci.so`,使得Qt应用能够通过OCI与Oracle数据库进行交互。这些步骤适用于具备一定Linux和Qt经验的开发者,并且能够为需要使用Qt开发数据库应用的专业人士提供指导。
216 1
讲解linux下的Qt如何编译oracle的驱动库libqsqloci.so
|
5月前
|
Linux 网络安全 API
【Azure 应用服务】App Service For Linux 环境中,如何从App Service中获取GitHub私有库(Private Repos)的Deploy Key(RSA key)呢?
【Azure 应用服务】App Service For Linux 环境中,如何从App Service中获取GitHub私有库(Private Repos)的Deploy Key(RSA key)呢?
|
7月前
|
存储 Linux C语言
Linux|如何安装和运行多个 glibc 库
Linux|如何安装和运行多个 glibc 库
1487 5
|
5月前
|
小程序 Linux 开发者
Linux之缓冲区与C库IO函数简单模拟
通过上述编程实例,可以对Linux系统中缓冲区和C库IO函数如何提高文件读写效率有了一个基本的了解。开发者需要根据应用程序的具体需求来选择合适的IO策略。
43 0
|
7月前
|
Linux 编译器 C语言
Linux中的pkg-config:简化库依赖管理的利器
**pkg-config**是Linux下管理库依赖的工具,它通过读取库的`.pc`文件提供编译和链接参数。使用`pkg-config --cflags --libs &lt;library&gt;`获取编译和链接选项,例如`gcc -o test test.c $(pkg-config --cflags --libs glib-2.0)`。能进行版本检查、参数提取、依赖管理和路径搜索。列出所有包用`pkg-config --list-all`。最佳实践包括确保库正确安装、检查版本、配置`PKG_CONFIG_PATH`及使用构建工具。
|
7月前
|
NoSQL Linux C语言
Linux gdb调试的时候没有对应的c调试信息库怎么办?
Linux gdb调试的时候没有对应的c调试信息库怎么办?
62 1

热门文章

最新文章