Linux 第二十六章

🐶博主主页:@ᰔᩚ. 一怀明月ꦿ 

❤️‍🔥专栏系列:线性代数C初学者入门训练题解CC的使用文章「初学」C++linux

🔥座右铭:“不要等到什么都没有了,才下定决心去做”

🚀🚀🚀大家觉不错的话,就恳求大家点点关注,点点小爱心,指点指点🚀🚀🚀

目录

动静态库

在库的制作者角度

ldd

静态库的制作

动态库的制作

站在库的使用者角度

怎么使用自己静态库

怎么使用自己动态库

动态库加载 


动静态库

静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库

动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。

一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码

在库的制作者角度

库里是没有 main 函数的,我们也不能把 main 函数写入库中

ldd

ldd 命令是 Linux 系统中的一个工具,用于打印一个可执行文件或共享库所依赖的共享库信息。它的用法很简单,只需在终端中输入 ldd 命令,后跟待检查的可执行文件或共享库的路径,即可显示出该文件所依赖的共享库列表。


例如,要查看可执行文件 example 所依赖的共享库,可以执行以下命令:

ldd example
ldd 命令输出的信息包括:
依赖的共享库的路径
共享库的名称
共享库的版本信息
共享库的地址等信息

这些信息可以帮助你确定一个程序运行所需的共享库是否已经正确安装,并且可以帮助你解决程序运行时出现的共享库缺失或版本不匹配的问题。

需要注意的是,ldd 命令只能检查动态链接的共享库,对于静态链接的库则无法查看。

静态库的制作

静态库,本质是将库中的源代码直接翻译成为.o目标二进制文件,然后打包
1)将目标文件编译成 .o 文件

[BCH@hcss-ecs-6176 dc]$ gcc -c add.c sub.c mul.c div.c
[BCH@hcss-ecs-6176 dc]$ ll
总用量 52
-rw-rw-r-- 1 BCH BCH   55 1月  22 14:16 add.c
-rw-rw-r-- 1 BCH BCH   59 1月  22 14:23 add.h
-rw-rw-r-- 1 BCH BCH 1240 1月  22 14:23 add.o
-rw-rw-r-- 1 BCH BCH   84 1月  22 14:16 div.c
-rw-rw-r-- 1 BCH BCH   59 1月  22 14:23 div.h
-rw-rw-r-- 1 BCH BCH 1248 1月  22 14:23 div.o
-rw-rw-r-- 1 BCH BCH   55 1月  22 14:16 mul.c
-rw-rw-r-- 1 BCH BCH   59 1月  22 14:23 mul.h
-rw-rw-r-- 1 BCH BCH 1240 1月  22 1:23 mul.o
-rw-rw-r-- 1 BCH BCH   55 1月  22 14:16 sub.c
-rw-rw-r-- 1 BCH BCH   59 1月  22 14:23 sub.h
-rw-rw-r-- 1 BCH BCH 1240 1月  22 14:23 sub.o

2)使用 ar 命令将目标文件打包成静态库的.a文件:

[BCH@hcss-ecs-6176 dc]$ ar -rc libmymath.a add.o sub.o mul.o div.o
libmymath.a:是生成静态库的名字,在 linux 中,静态库的名字有前缀和后缀,
前缀必须lib开头,后缀必须.a结尾

动态库的制作

1)将目标文件编译成 .o 文件

[BCH@hcss-ecs-6176 dc1]$ gcc -fPIC -c add.c sub.c mul.c div.c
//在使用 -fPIC 选项编译代码时,编译器会生成与具体内存地址无关的代码,使得代码可以在不同的地址空间中加载和执行。这样,生成的可执行文件或共享库就更具通用性和灵活性。
[BCH@hcss-ecs-6176 dc1]$ ll
总用量 48
-rw-rw-r-- 1 BCH BCH   55 1月  22 15:31 add.c
-rw-rw-r-- 1 BCH BCH   59 1月  22 15:31 add.h
-rw-rw-r-- 1 BCH BCH 1240 1月  22 15:32 add.o
-rw-rw-r-- 1 BCH BCH   84 1月  22 15:31 div.c
-rw-rw-r-- 1 BCH BCH   59 1月  22 15:31 div.h
-rw-rw-r-- 1 BCH BCH 1248 1月  22 15:32 div.o
-rw-rw-r-- 1 BCH BCH   55 1月  22 15:31 mul.c
-rw-rw-r-- 1 BCH BCH   59 1月  22 15:31 mul.h
-rw-rw-r-- 1 BCH BCH 1240 1月  22 15:32 mul.o
-rw-rw-r-- 1 BCH BCH   55 1月  22 15:31 sub.c
-rw-rw-r-- 1 BCH BCH   59 1月  22 15:31 sub.h
-rw-rw-r-- 1 BCH BCH 1240 1月  22 15:32 sub.o

2)以使用 GCC 将位置无关的目标文件链接到一个动态库中:

[BCH@hcss-ecs-6176 dc1]$ gcc -shared -o libmymath.so add.o sub.o mul.o div.o
libmymath.so:是生成动态库的名字,在 linux 中,动态库的名字有前缀和后缀,
前缀必须lib开头,后缀必须.so结尾

站在库的使用者角度

静态库,我们自己写的是第三方库,包含.h文件和.a(.o的集合)

gcc 默认动态链接的,如果你只提供.a,gcc 也没有办法,只能局部性的把你指定的.a,进行静态链接,其他库正常正常动态链接,如果gcc加上-static,就必须要.a

怎么使用自己静态库

为了使自己的静态库更加标准,我们得封装一下
1)我们得创建一个 mymath_lib 的目录,mymath_lib里面也包含两个目录,inlude 和 lib。include 目录里保存是头文件 ,lib里面保存的是.a文件

[BCH@hcss-ecs-6176 mymath_lib]$ tree .
.
├── include
│   ├── add.h
│   ├── div.h
│   ├── mul.h
│   └── sub.h
└── lib
    └── libmymath.a

2)使用我们的静态库
在 test.c中使用了add等函数,而 add 等函数在我们的静态库中

#include"add.h"
#include"sub.h"
#include"mul.h"
#include"div.h"
int main()
{
        int x=10;
        int y=20;
        printf("%d + %d = %d\n",x,y,add(x,y));
        printf("%d - %d = %d\n",x,y,sub(x,y));
        printf("%d * %d = %d\n",x,y,mul(x,y));
        printf("%d / %d = %d\n",x,y,div(x,y));
}

那我们怎么编译我们的 test.c文件呢

我们要把我们封装好的静态库mymath_lib放到和 test.c一个目录下

[BCH@hcss-ecs-6176 dc]$ gcc test.c -I mymath_lib/include -l mymath -L mymath_lib/lib
-I mymath_lib/include:链接我们的头文件
-l mymath -L mymath_lib/lib:-l mymath链接我们的.a文件,-L mymath_lib/lib:指定链接.a文件的路径

我们在mymath_lib/lib 目录下存的libmymath.a文件,我们使用的.a文件的文件名是 mymath

怎么使用自己动态库

为了使自己的动态库更加标准,我们得封装一下
1)我们得创建一个 mymath_lib 的目录,mymath_lib里面也包含两个目录,inlude 和 lib。include 目录里保存是头文件 ,lib里面保存的是.so文件

[BCH@hcss-ecs-6176 mymath_lib]$ tree .
.
├── include
│   ├── add.h
│   ├── div.h
│   ├── mul.h
│   └── sub.h
└── lib
    └── libmymath.so

2)使用我们的动态库
在 test.c中使用了add等函数,而 add 等函数在我们的动态库中

#include"add.h"
#include"sub.h"
#include"mul.h"
#include"div.h"
int main()
{
        int x=10;
        int y=20;
        printf("%d + %d = %d\n",x,y,add(x,y));
        printf("%d - %d = %d\n",x,y,sub(x,y));
        printf("%d * %d = %d\n",x,y,mul(x,y));
        printf("%d / %d = %d\n",x,y,div(x,y));
}

那我们怎么编译我们的 test.c文件呢

我们要把我们封装好的动态库mymath_lib放到和 test.c一个目录下
[BCH@hcss-ecs-6176 dc]$ gcc test.c -I mymath_lib/include -l mymath -L mymath_lib/lib

-I mymath_lib/include:链接我们的头文件
-l mymath -L mymath_lib/lib:-l mymath链接我们的.so文件,-L mymath_lib/lib:指定链接.so文件的路径

第一种方法

接下来我们还需要把.h文件和.so文件放到 os 自带的库里
[BCH@hcss-ecs-6176 dc1]$ sudo cp mymath_lib/include/*.h /usr/include
[BCH@hcss-ecs-6176 dc1]$ sudo cp mymath_lib/lib/*.so /lib64
用 ldd 命令查看我们动态链接libmymath.so
[BCH@hcss-ecs-6176 dc1]$ ldd a.out
        linux-vdso.so.1 =>  (0x00007ffd3d7a2000)
        libmymath.so => /lib64/libmymath.so (0x00007f547b350000)
        libc.so.6 => /lib64/libc.so.6 (0x00007f547af82000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f547b552000)

第二种方法

在当目录下创建软连接
[BCH@hcss-ecs-6176 dc1]$ ls -li
总用量 16
2753548 -rwxrwxr-x 1 BCH BCH 8504 1月  22 16:10 a.out
2753538 lrwxrwxrwx 1 BCH BCH   27 1月  22 16:07 libmymath.so ->mymath_lib/lib/libmymath.so
2753540 drwxrwxr-x 4 BCH BCH 4096 1月  22 15:48 mymath_lib

第三种方法

使用环境变量的方式,让 os 找到动态库.so
[BCH@hcssecs6176dc1]$export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/BCH/1_21/dc1/mymath_lib/lib
LD_LIBRARY_PATH环境变量
第四种方法
直接更改os 关于动态库的配置文件/etc/ld.so.conf.d/
1)添加一个.conf的文件
[BCH@hcss-ecs-6176 dc1]$ sudo touch /etc/ld.so.conf.d/math.conf
2)然后在.conf文件中添加一个.so的路径
[BCH@hcss-ecs-6176 dc1]$ cat /etc/ld.so.conf.d/math.conf
/home/BCH/1_21/dc1/mymath_lib/lib

同一组库,同时提供动态库和静态库,默认是使用动态库

动态库加载 

gcc -fPIC -c xxx.c//fPIC与位置无关码

1)程序加载的时候,库也要加载

2)程序没有被加载,程序内部有地址吗?
有的

变量名,函数名等,编译成为2 进制,全都都成了地址
在 C 语言中,变量和函数在编译期间会被编译成汇编代码,然后再由汇编器将汇编代码转换为二进制数据,最终生成可执行文件。在可执行文件中,变量和函数的实现代码已经被编译成二进制形式,而变量和函数名则以符号表的形式存储在可执行文件中。

当程序在运行时,操作系统会将可执行文件加载到内存中,并将其中的符号表信息加载到进程的符号表中。程序可以通过符号名称访问到对应的函数和变量的实现代码。

编译的时候,对代码进行编址,基本遵守虚拟地址空间那一套
虚拟地址空间,不仅仅是 OS 里面的概念。编译器编译的时候,也要按照这样的规则编译可执行程序,这样才能在加载的时候,进行从磁盘文件到内存,再进行映射


 3)绝对地址,相对地址
绝对地址:平坦模式,就和我们以前学的虚拟地址
相对地址:库函数的编址(与位置无关码有关)

动态链接链接的是动态库,而动态库中包含了大量的常用的功能接口指令代码

这种链接方式,是用于解决静态库存在的浪费内存和磁盘空间,以及模块更新困难等问题。

动态链接生成可执行程序,可执行程序中会记录自己依赖的库列表以及库中的函数地址信息,等到运行程序的时候,由操作系统将库加载到内存中(多个程序可以共享,不需要加载多份相同实例),然后根据库加载后的地址在对每个程序内部用到的库函数的地址进行偏移计算。

基于这么一种思想,动态链接具有以下优缺点:

更加节省内存并减少页面交换;

库文件与程序文件独立,只要输出接口不变,更换库文件不会对程序文件造成任何影响,因而极大地提高了可维护性和可扩展性;

不同编程语言编写的程序只要按照函数调用约定就可以调用同一个库函数;

适用于大规模的软件开发,使开发过程独立、耦合度小,便于不同开发者和开发组织之间进行开发和测试。

运行时依赖,否则找不到库文件就会运行失败

运行加载速度相较静态库慢一些

需要对库版本之间的兼容性做出更多处理

 🌸🌸🌸如果大家还有不懂或者建议都可以发在评论区,我们共同探讨,共同学习,共同进步。谢谢大家! 🌸🌸🌸 

相关推荐

  1. Linux 第二

    2024-05-11 17:02:04       11 阅读
  2. 第二 版本管理 - GIT

    2024-05-11 17:02:04       13 阅读
  3. Linux

    2024-05-11 17:02:04       12 阅读
  4. 第二 :Docker 内部 DNS 服务如何使用

    2024-05-11 17:02:04       22 阅读
  5. 第二 配置 Web Gateway 的默认参数

    2024-05-11 17:02:04       18 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-05-11 17:02:04       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-05-11 17:02:04       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-05-11 17:02:04       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-05-11 17:02:04       18 阅读

热门阅读

  1. vue3-seamless-scroll实现循环滚动

    2024-05-11 17:02:04       8 阅读
  2. 以太网网络变压器型号

    2024-05-11 17:02:04       10 阅读
  3. git 更换远程仓库地址三种方法总结

    2024-05-11 17:02:04       9 阅读
  4. 【笔记】Android MVNO APN 字段配置方法

    2024-05-11 17:02:04       9 阅读
  5. react18【系列实用教程】useState (2024最新版)

    2024-05-11 17:02:04       12 阅读
  6. impdp恢复表后发现比原表多了100多行

    2024-05-11 17:02:04       9 阅读
  7. C++ 多线程笔记1 线程的创建

    2024-05-11 17:02:04       10 阅读
  8. 【Python快速上手(十九)】

    2024-05-11 17:02:04       6 阅读
  9. 【go从入门到精通】精通并发编程-协程goroutine

    2024-05-11 17:02:04       12 阅读
  10. 【算法入门教育赛2】C.曼哈顿种类 C++题解与代码

    2024-05-11 17:02:04       12 阅读
  11. 安卓提示Cannot resolve symbol ‘BuildConfig‘

    2024-05-11 17:02:04       13 阅读
  12. 数据结构解构方法

    2024-05-11 17:02:04       37 阅读