Linux 动静态库

news/2024/9/22 22:32:31/

目录

一.静态库

1.理解静态库

a.什么是静态库?

b.创建静态库的理论?

2.打包静态库

3.静态库的使用方法

a.头文件找不着

b.链接报错——库函数文件找不着

4.将静态库文件写到系统目录下

a.直接拷贝

b.建立软链接

二.动态库

1.什么是动态库?

2.打包动态库

3.动态库的使用方法

4.静态链接和动态链接

5.解决方案

环境变量

三.动态库加载(底层原理)

1.前置知识

2.动态库加载(首地址+偏移地址)


一.静态库

1.理解静态库

a.什么是静态库?

简单来说,就是把一些功能接口(函数)打包到一个库里,供他人使用,当第三方使用库内的某一函数时,函数的实现会以拷贝的形式插入代码中,这种函数库,我们将其称为静态库。

b.创建静态库的理论?

我们知道,将一个.c源文件转换成一个可执行程序,需要经过 编译 和 链接 这两个步骤。即,将.c文件编译成.o文件,再将可能用到的所有.o文件链接起来,生成可执行程序。

所以,如果我们想要打包一个静态库,首先要将所有的.c文件编译成.o文件,然后在把所有的.o文件用特定的指令打包成一个库文件!

2.打包静态库

把包含库函数的多个.c文件用 gcc -c file.c 命令编译相应的.o文件,然后用ar -rc libmymath.a file.o 命令将所有.o文件打包成库的形式

如果我们想要将上述的库提供给他人使用,除了需要将 库函数文件(libmymath.a)提供给对方外,还需要将库函数所用到的头文件(Add.h、Del.h、Mul.h、Sub.h)提供给对方。

所以,我们可以将静态库分成两部分:①库函数文件(libmymath.a);②头文件。如果我们将这两部分放在同一个目录下,并将该目录压缩打包,然后就能把这个压缩包发给被人使用啦~~

3.静态库的使用方法

接下来,我们站在对方(使用我们提供的静态库的人)的视角,来看看第三方静态库究竟是如何使用的~~

a.头文件找不着

首先肯定是对压缩包进行解压操作,得到一个目录,该目录下有库函数文件(libmymath.a)和头文件,如下图所示:

接下来,我们创建一个test.c文件,并在该文件中使用静态库所提供的功能接口,试一试~~

那么,编译test.c文件,能否成功呢?

出错啦~~

头文件目录明明就在mymath_lib目录中,系统为什么还说找不到呢??

原因:系统在展开头文件时,只会到两个地方找:①系统的 /user/include/ 目录;②文件所在当前目录。若两个地方都找不到,则报错!!

虽然 mymath_lib 与 test.c 处在同一个目录下,但 Add.h 可不在这里!!

解决方法:

b.链接报错——库函数文件找不着

再次编译 test.c 再试一试~~

报错原因:系统在链接时,会去 /lib64/ 目录下寻找库函数文件,找不着,就报错!!

解决方法:

编译时可以用 -I 指定“头文件”路径、用-L指定“静态库”路径、用 -l 指定所链接的“目标库名”

运行试一试~~

成功啦~~

虽然使用成功了,但是... 每次使用这个库时都要以相对路径的方式包含头文件,而且,编译时还得加上-L 这样的字段,是不是太麻烦了? 嗯~~ 有木有一劳永逸的解决方法嘞? --- 哎,还真有!!

4.将静态库文件写到系统目录下

系统头文件所在的目录: /usr/include/

系统库函数文件所在目录: /usr/local/lib/

由于系统在展开头文件时,会去 /user/include/ 目录下查找;编译时,会去 /lib64/ 目录下查找。所以,我们只需要将头文件和库函数文件分别拷贝到系统头文件目录和系统库函数目录即可~~

a.直接拷贝

sudo cp  ./mymath_lib/include/*  /usr/include/    (拷贝头文件)

sudo cp  ./mymath_lib/lib/*  /usr/local/lib/  (拷贝库函数文件)

b.建立软链接

在 /user/include/ 和 /lib64/ 目录下建立软链接(注:软链接指向的路径必须是绝对路径!!)

给头文件建立软链接(在 /user/include/ 目录下操作):

sudo ln -s /home/common_whb/lesson29/static_warehouse/mymath_lib/include/Add.h  Add.h

给库函数文件建立软链接(在 /usr/local/lib/  目录下操作):

sudo ln -s /home/common_whb/lesson29/static_warehouse/mymath_lib/libmymath.a  libmymath.a

值得一提的是,上述两种方法虽然可以让我们在编译源文件时,无需使用指令(-I 和 -L)的方式指明路径,但在编译时,我们仍需使用 -l(小写L,前面那个是大写i)来指明第三方库名。

二.动态库

1.什么是动态库?

--- 本质就是将可能用到的.c文件直接翻译称为.o二进制文件,然后打包成的库,供第三方使用,当第三方使用库内的某一函数时,函数代码会以动态链接的形式被他人使用。

2.打包动态库

把所有的.c文件编译成.o文件的命令:gcc -fPIC -c  *.c

把所有的.o文件打包成动态库的命令:gcc -shared -o libmymath.so *.o

把包含库函数的多个.c文件用 gcc -fPIC -c file.c 命令编译相应的.o文件,然后用 gcc -shared -o libmymath.so *.o 命令将所有.o文件打包成 libmymath.so 库

3.动态库的使用方法

接下来,我们依旧站在对方(使用我们提供的静态库的人)的视角,来看看第三方动态库究竟是如何使用的~~

我们会遇到与静态库的使用相似的问题,即:①头文件找不到;②链接报错(库函数文件)

这两个问题的解决方法与静态库一摸一样的,咱们在这里就不再赘述了,直接一行代码搞定~~

gcc test.c -o test -I(大写的i) dynamic_warehouse/mymath_lib/include -L dynamic_warehouse/mymath_lib/lib -l(小写的L) mymath

执行试一试~~

源文件明明已经成功编译成了可执行程序,但运行可执行程序时,为什么还要去找动态库?

哎~~这就要说说静态链接和动态链接的区别了~~

4.静态链接和动态链接

静态链接:链接静态库时,系统会将相关代码和数据直接拷贝到己方源文件中,而后生成可执行程序,这样一来,可执行程序的运行便与静态库再无任何联系。

动态链接:链接动态库时,己方源文件内只会记录调用函数在动态库内的偏移地址,大大减少己方文件代码量的同时,却也提高了可执行程序对动态库的依赖性,即,当我们运行可执行程序时,仍需知道动态库的路径,并将动态库也一并加载到内存!!

gcc默认是动态链接的,但个别库,如果只提供.a(静态库),gcc也没办法,只能局部的把你指定的.a进行静态链接,其他库正常动态链接,而如果命令行有 -static, 无论有无动态库,都会执行静态链接。

所以,当使用静态库的源文件编译形成可执行程序后,可执行程序的运行就和静态库没关系了;而使用动态库编译形成可执行程序后,可执行程序的运行仍需要知道动态库的路径!!

故,由于动态链接生成的可执行程序的运行,仍需知道动态库的路径,所以,用指令(-I 和 -L)的方式编译源文件便不再可行(没法运行程序)了。

5.解决方案

解决方法:①直接拷贝(方法如静态库);②建立软链接(方法如静态库)。这两种方法既能编译源文件形成可执行程序,又能直接运行可执行程序。

而除了上述两种解决方法外,我们还可以用环境变量的方式来添加链接器默认搜索路径上的文件,注意:是链接器(针对的是库函数文件,不包括头文件),不是系统,头文件依旧需要指定路径。

环境变量

使用环境变量LD_LIBRARY_PATH,让系统找到自己的动态库。

虽然LD_LIBRARY_PATH这个环境变量主要用于动态链接器在运行时搜索动态库(.so文件),但在某些情况下,它也可能间接影响链接器在编译时的行为

操作如下:

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/common_whb/lesson29/dynamic_warehouse/mymath_lib/lib

即,将包含 .so 文件的目录添加到 LD_LIBRARY_PATH,而不是将 .so 文件本身添加到该环境变量中。

注意:若使用该方法,待xshell外壳程序退出重新登陆时,环境变量会被清除。

小知识:ldd  a.out  查看一个可执行程序所链接的动态库 

三.动态库加载(底层原理)

1.前置知识

源文件编译成可执行程序的时候,代码被翻译成二进制文件后,原先代码中的变量名和函数名等都变成了一个个逻辑地址,链接动态库时,这些逻辑地址会记录动态库名和库函数的偏移地址,最终形成ELF格式的可执行程序!!

什么是库函数的偏移地址?--- 就是动态库中某一函数相对于该库首地址的偏移量,-fPIC (与位置无关码)的作用,就是让系统用偏移量的方式对库中的函数进行编址。

Linux下,可执行程序是ELF格式的,动态链接的可执行程序,不光程序本身要加载到内存,链接的库也要加载,并且,动态库在加载到内存后,会被所有进程共享!!

ELF格式的可执行文件(二进制文件格式):文件内包含了程序的机器码、数据、符号表、重定位信息以及程序运行所需的其他元数据。

2.动态库加载(首地址+偏移地址)

---源文件能够链接动态库的原因是源文件在翻译的过程中与动态库形成了某种关联(其实就是在可执行程序内记录下了的动态库名和方法偏移量),

由于调用了动态库的源文件形成的可执行程序运行时,仍需要使用动态库,那么,可执行程序是如何使用动态库的呢??

首先,可执行程序在加载到内存后,会在页表中填充虚拟地址和物理地址的映射关系,CPU会读取其表头中的entry代码入口地址,拿到虚拟地址块空间中代码段的入口地址,随后CPU上的指令寄存器便可读取地址空间上的代码,当读取到调用库函数的代码时,OS会查看该动态库是否已经加载到内存(其它进程可能在使用),若其尚未被加载到内存,则加载。

加载动态库的具体细节?

当动态库加载到物理内存后,物理地址与程序地址空间上的虚拟地址通过页表映射,在程序地址空间上的库的首地址会替换库名,通过库首地址,可以在程序地址空间上找到这个库,再通过方法偏移量,可以找到动态库内的某一具体函数

动态库在被映射到的共享区上的位置不是固定的!!


http://www.ppmy.cn/news/1529038.html

相关文章

Cassava Network 获得 Anubi 基金会战略投资,加速构建 Web3 领先平台

Cassava Network 作为一个以 Web3 社交任务为核心的平台,近日获得了来自 Anubi 基金会的战略投资。这一投资标志着 Cassava 在推动其成为 Web3 最大流量入口的使命上迈出了重要的一步。 Anubi 基金会战略投资 Anubi 基金会专注于 Web3 生态系统的应用,…

2024年自学手册 网络安全(黑客技术)

🤟 基于入门网络安全/黑客打造的:👉黑客&网络安全入门&进阶学习资源包 前言 什么是网络安全 网络安全可以基于攻击和防御视角来分类,我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术,而“蓝队”、…

【计算机网络篇】数据链路层 功能|组帧|流量控制与可靠传输机制

🧸安清h:个人主页 🎥个人专栏:【计算机网络】 🚦作者简介:一个有趣爱睡觉的intp,期待和更多人分享自己所学知识的真诚大学生。 系列文章目录 【计算机网络篇】计算机网络概述 【计算机网络篇…

Linux下的简单TCP客户端和服务器

客户端 #include <arpa/inet.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/socket.h>int main() {struct sockaddr_in* caddr(struct sockaddr_in*)malloc(sizeof(struct sockaddr…

MySQL篇(索引)(持续更新迭代)

目录 一、简介 二、有无索引情况 1. 无索引情况 2. 有索引情况 3. 优劣势 三、索引结构 1. 简介 2. 存储引擎对于索引结构的支持情况 3. 为什么InnoDB默认的索引结构是Btree而不是其它树 3.1. 二叉树&#xff08;BinaryTree&#xff09; 3.2. 红黑树&#xff08;RB&a…

【系统架构设计师】设计模式的分类

设计模式概述 设计模式(Design Pattern)是软件开发中的最佳实践,旨在解决常见的设计问题。它们可以分为三大类:创建型模式、结构型模式、行为型模式,每个类别都提供了解决特定问题的模式。下面将详细介绍每个类别及其包含的所有设计模式,并提供简要的说明,帮助区分不同…

螺栓与散装物体检测系统源码分享

螺栓与散装物体检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Comput…

C++从入门到起飞之——多态 全方位剖析!

&#x1f308;个人主页&#xff1a;秋风起&#xff0c;再归来~&#x1f525;系列专栏&#xff1a;C从入门到起飞 &#x1f516;克心守己&#xff0c;律己则安 目录 1. 多态的概念 2. 多态的定义及实现 2.1 多态的构成条件 2.1.1 实现多态还有两个必须重要条件&…