【Linux】深刻理解动静态库

1.什么是库

库是写好的现有的,成熟的,可以复⽤的代码。现实中每个程序都要依赖很多基础的底层库,不可能每个⼈的代码都从零开始,因此库的存在意义⾮同寻常。本质上来说库是⼀种可执⾏代码的⼆进制形式,可以被操作系统载⼊内存执⾏。库有两种:
  • 静态库 .a[Linux]、.lib[windows]
  • 动态库 .so[Linux]、.dll[windows]

2.手动制作静态库

如果我们要做一个静态库的话,首先我们需要把我们源文件(.c/.cpp)隐藏起来,头文件(.h)必须暴露出来,
1.我们先要把我们所有的.c文件编译成.o文件,因为我们都知道我们正常进行程序翻译的时候,首先要做的工作就是把.c变成.o文件,最后把所有.o都链接起来。
2.把所以.o文件打包
指令:ar -rc
ar是 Unix 和类 Unix 系统下用于创建、修改和提取归档文件的工具,
ar -rc是
ar命令的一个常用选项组合,其功能如下:
  • -r(replace):该选项用于将文件替换或插入到归档文件中。如果归档文件中已存在同名文件,-r选项会用新文件替换旧文件;如果归档文件中不存在同名文件,则将新文件插入到归档文件中。
  • -c(create):用于创建一个新的归档文件。若指定的归档文件已存在,-c选项不会报错,而是静默处理,使得ar -rc能够在归档文件不存在时创建新的归档文件,存在时进行更新操作。
语法:
ar [选项] 归档文件 目标文件...
  • 归档文件:指定要创建或修改的归档文件的名称,通常具有.a扩展名,如libexample.a
  • 目标文件:指定要添加到归档文件中的一个或多个源文件的名称,可以是 C、C++ 源文件,也可以是目标文件(.o文件)等。
  • 静态库必须有前缀lib,后缀.a。
这个库制作好了,直接把他安装到系统里面:
把我们头文件拷贝到系统特定路径里,Linux系统里默认搜索头文件路径是/usr/include
然后把我们库也要安装到系统里,默认路径一般是/lib64/
这样静态库和头文件都被安装到系统中去了,所以Linux系统中,所有的安装本质就是拷贝。

3.手动调用我们自己的静态库

然后试试别人能不能直接使用头文件中的方法:
创建一个other目录:
#include<my_stdio.h>
#include<my_string.h>
#include<stdio.h>
int main()
{
    const char *a = "abcdf";
    printf("%s: %d\n",a,my_strlen(a));
    myFILE *fp =mfopen("./log.txt","a");
    if(fp ==NULL) return 1;
    mfwrite(a,my_strlen(a),fp);
    mfwrite(a,my_strlen(a),fp);
    mfwrite(a,my_strlen(a),fp);
    mfclose(fp);
    return 0;
}
为什么会链接报错呢?
因为编译器找不到,那同样是库,那为什么那么多库,能找到libc.aC语言的库呢?为什么不要告诉编译器呢?所以那是因为gcc是编C语言的,他默认要认识C标准库的,我们刚才写的库叫第三方库,如何要让编译器认识?刚才头文件和库都拷贝到系统,都能被找到,但是库有那么多,究竟应该链接哪一个库呢,所以
-l 这个l就是link(链接),然后库名libmystdio.a取掉前缀后缀就是stdio,所以这样就能找到库,并且能使用
-l就是引入指定名称的第三方库。
我们也可以不用把这些安装到系统里,别人直接给我们提供了头文件和库,这时我们该怎么使用呢?
指令:gcc main.c -o main -L. -lstdio
-L 告诉编译器,编译的时候,查找库,除了在系统路径下查找,也要在指定路径下查找
  • gcc:是 GNU Compiler Collection 的缩写,是一个广泛使用的开源编译器套件,支持多种编程语言,这里主要用于编译 C 语言程序。
  • main.c:是要编译的源文件,通常包含 C 语言的源代码。
  • -o main:-o选项用于指定输出文件的名称,这里将编译后的可执行文件命名为main。如果不使用-o选项,默认的输出文件名是a.out
  • -L.:-L选项用于指定库文件的搜索路径。这里的.表示当前目录,即告诉编译器在当前目录下搜索库文件。
  • -lstdio:-l选项用于指定要链接的库。这里的stdio是库的名称(去掉lib前缀和.a.so后缀),编译器会在-L指定的路径(这里是当前目录)下搜索名为libstdio.alibstdio.so的库文件,并将其链接到可执行文件中。
所以我们把库写好了,库里面应不应该有没有main函数?
1.库里面绝对不能有main函数, 如果库中也有main 函数,那么在链接库到其他程序时,就会出现多个main 函数,这会导致链接错误,因为程序不知道该从哪个main 函数开始执行。
2.库我们在发布的时候,我们建议,把所有的头文件放在include目录下,把我们库放在lib64目录下。
通过makefile来制作静态库:
libmystdio.a:my_stdio.o my_string.o
    ar -rc $@ $^
%.o:%.c
    gcc -c $<
.PHONY:clean
clean:
    rm -f *.a *.o       
发布我们写的库:
指令:tar
tar是 Unix 和类 Unix 系统中常用的归档工具,用于将多个文件或目录打包成一个归档文件,也可以从归档文件中提取文件或目录。以下是关于
tar的详细介绍:

基本功能

  • 打包:可以将多个文件和目录组合成一个单一的归档文件,方便存储、传输和备份。
  • 压缩:通常与压缩工具(如gzipbzip2等)结合使用,在打包的同时进行压缩,减小文件占用的存储空间。
  • 解压:从归档文件中恢复出原始的文件和目录结构。

常用命令选项

  • -c(create):创建新的归档文件。例如:tar -cvf archive.tar file1 file2 dir1,这会将file1file2dir1打包到archive.tar中。
  • -x(extract):从归档文件中提取内容。例如:tar -xvf archive.tar,会将archive.tar中的文件和目录提取到当前目录。
  • -t(list):列出归档文件中的内容,但不提取。例如:tar -tvf archive.tar,会显示archive.tar中包含的文件和目录列表。
  • -f(file):指定归档文件的名称。这个选项必须紧跟在-c-x-t之后,后面跟上归档文件的路径和名称。
  • -z(gzip):使用gzip进行压缩或解压。例如:tar -czvf archive.tar.gz file1 file2,会创建一个gzip压缩的归档文件archive.tar.gztar -xzvf archive.tar.gz,会解压gzip压缩的归档文件。
  • -j(bzip2):使用bzip2进行压缩或解压。例如:tar -cjvf archive.tar.bz2 file1 file2,会创建一个bzip2压缩的归档文件archive.tar.bz2tar -xjvf archive.tar.bz2,会解压bzip2压缩的归档文件。
libmystdio.a:my_stdio.o my_string.o
    ar -rc $@ $^
%.o:%.c
    gcc -c $<
.PHONY:clean
clean:
    rm -rf *.a *.o stdc
.PHONY:output
output:
    mkdir -p stdc/include
    mkdir -p stdc/lib
    cp -f *.h stdc/include
    cp -f *.o stdc/lib
    tar -czf stdc.tgz stdc

操作使用:
然后怎么使用呢?
  • gcc:GNU Compiler Collection 的缩写,是一个广泛使用的编译器,用于编译 C 语言等程序。
  • main.c:要编译的源文件,其中包含了 C 语言的源代码。
  • -o main:-o选项用于指定输出文件的名称,这里将编译后的可执行文件命名为main。如果不使用-o选项,默认的输出文件名是a.out
  • -I stdc/include:-I(大写的 i)选项用于指定头文件的搜索路径。这里告诉编译器在stdc/include目录下搜索main.c#include指令所包含的头文件。例如,如果main.c中有#include "myheader.h",编译器会在stdc/include目录中查找myheader.h文件。
  • -L stdc/lib:-L选项用于指定库文件的搜索路径。这意味着编译器会在stdc/lib目录下搜索后续-l选项指定的库文件。
  • -l mystdio:-l(小写的 L)选项用于指定要链接的库。这里的mystdio是库的名称(去掉lib前缀和.a.so后缀),编译器会在-L指定的路径(即stdc/lib)下搜索名为libmystdio.alibmystdio.so的库文件,并将其链接到可执行文件main中。

4.手动制作动态库

动态库前缀也要加lib,后缀加.so
gcc/g++不仅能形成可执行程序,还能形成动态库
gcc -shared  用于生成共享库(shared library),在 Linux 和类 Unix 系统中,共享库是一种可执行代码的二进制形式,能被多个程序同时使用,以节省内存并实现代码复用。
形成动态库时,形成.o的过程要新增一个选项 -fPIC,形成与位置无关码
-fPIC是 GCC 编译器中的一个重要选项,全称为 “Position - Independent Code”,即生成位置无关代码。以下从多个方面详细介绍它:

1. 作用原理

在编译生成共享库(shared library)或可执行文件时,程序的代码和数据在内存中的加载位置会影响指令的执行。传统的可执行文件在编译时,代码中的内存地址引用是基于固定的加载地址计算的。但共享库比较特殊,它可能被多个不同的程序加载到不同的内存地址空间运行。
-fPIC选项会让编译器生成的代码,不依赖于特定的内存加载地址。编译器通过使用相对地址而不是绝对地址来引用数据和代码,这样生成的代码可以在内存的任意位置加载执行,而不需要在加载时进行重定位(relocation)。

2. 为何重要

  • 共享库的需求:共享库是为多个程序所共享的代码集合。如果没有-fPIC生成位置无关代码,那么每个使用该共享库的程序在加载共享库时,都需要对共享库中的代码进行重定位,这不仅增加了加载时间,而且还可能导致内存使用效率低下。通过-fPIC,共享库可以在不同进程的地址空间中共享相同的物理内存页面,极大地节省了内存资源。
  • 动态链接的基础:现代操作系统普遍采用动态链接机制,允许程序在运行时动态加载所需的共享库。-fPIC生成的代码使得动态链接过程更加高效和灵活,能够适应不同的内存布局和加载场景。
libmystdio.so:my_stdio.o my_string.o
    gcc -o $@ $^ -shared
%.o:%.c
    gcc -c $< -fPIC
.PHONY:clean
clean:
    rm -rf *.so *.o stdc

指令:ldd

ldd是一个在 Linux 和类 Unix 系统中非常有用的命令,主要用于查看程序运行时所依赖的共享库(shared libraries)。

1. 基本语法

ldd [选项] <文件名>
其中,
<文件名>是要检查的可执行文件或共享库的名称,选项可以用来获取更详细或特定格式的输出。

3. 常用选项

  • -v(详细模式)
    • 提供更详细的信息,包括共享库的版本号、加载的起始地址等。

4.输出解释

ldd 的输出通常包含以下信息:

  • 库名称:这是程序或共享库所依赖的动态库的名称,通常以 lib 开头,以 .so 结尾。
  • 库的完整路径:显示了动态库在文件系统中的实际位置。如果显示 not found,表示该动态库未找到,这可能会导致程序无法正常运行。
  • 库的地址:显示了该动态库在内存中的加载地址。这通常在运行时由动态链接器分配,对于大多数用户来说,这个信息可能不太重要,除非你正在进行一些系统级的调试。

3.如果把动态库删了,可执行程序会怎么样?

  1. 立即崩溃(在某些情况下)

- 如果可执行程序在启动时就需要加载被删除的动态库,并且系统无法找到合适的替代库来满足依赖关系,那么程序在启动阶段就会崩溃。例如,一个程序依赖于一个特定版本的图形库来进行界面初始化,当这个图形库动态文件被删除后,程序在启动时尝试加载该库就会失败,从而无法正常启动。
- 这是因为动态库中的函数和数据是在程序运行时按需加载的,当找不到所需的动态库时,操作系统无法完成程序的初始化过程

  1. 运行时出错

- 即使程序成功启动,如果在运行过程中需要调用被删除的动态库中的函数,也会出现错误。例如,一个计算程序在运行过程中需要调用动态库中的数学计算函数,当动态库被删除后,一旦执行到调用该函数的代码部分,就会出现 “无法找到动态库” 或 “无法解析符号(函数名)” 等错误。
- 这种情况通常会导致程序的部分功能失效或者直接崩溃,具体取决于程序对该动态库的依赖程度以及如何处理动态库加载失败的情况。有些程序可能会有一定的错误处理机制,能够在检测到动态库缺失时给出友好的错误提示并尝试恢复或继续执行其他功能,但很多程序没有这样的机制,就会直接异常终止。

如果把静态库删了,可执行程序会怎么样?
  1. 基本不受影响(一般情况)

- 当可执行程序链接了静态库后,库中的代码和数据会在编译和链接阶段被复制到可执行文件中。所以,在程序已经成功生成后,删除静态库通常不会影响可执行程序的正常运行。
- 例如,一个简单的 C 语言程序main.c链接了一个静态库libmath.a(其中包含了一些数学计算函数)来计算数值。在编译链接过程中,gcc main.c -o main -L. -lmath(假设库在当前目录下),libmath.a中的相关代码被整合进了main这个可执行文件。之后,即使删除了libmath.a,main依然可以正常运行,因为它已经拥有了运行所需的全部代码。

同时存在动静态库,默认用的是动态库,除非加 -static 

一、默认使用动态库

  • 编译时
    在类 Unix 系统(如 Linux)中,当同时存在一个库的静态版本(例如 libexample.a)和动态版本(例如 libexample.so),并且在编译链接可执行程序时没有特别指定使用静态库,编译器通常会默认使用动态库。例如,如果你有以下命令:
    gcc main.c -L. -lexample -o main
    

    这里 -L. 表示在当前目录查找库文件,-lexample 表示链接 libexample 库,编译器会优先使用 libexample.so(如果存在),因为这是默认行为。这是因为使用动态库可以节省磁盘空间和内存,因为多个程序可以共享同一个动态库,并且在更新库时,只需要更新动态库而不需要重新编译程序。

二、使用静态库

  • 使用 -static 选项
    如果你想强制使用静态库,可以使用 -static 选项。例如:
    gcc main.c -L. -lexample -o main -static
    

    此命令将强制编译器使用静态库 libexample.a 而不是 libexample.so 进行链接。这样,最终生成的可执行程序将包含静态库的代码,而不依赖于外部的动态库,其优点是可执行程序的独立性更强,可在不同环境中运行而不依赖于特定的库版本,但缺点是可执行程序的体积会变大。

三、库搜索顺序

  • 搜索顺序
    1. 编译器首先会搜索动态库。如果没有使用 -static 选项,它会优先使用动态库。
    2. 编译器会根据 -L 选项指定的路径查找库文件。如果没有使用 -L,则会按照系统的默认库搜索路径查找,通常包括 /usr/lib/usr/local/lib 等。
    3. 对于动态库,系统会在运行时根据 LD_LIBRARY_PATH 环境变量、/etc/ld.so.conf 文件和系统默认的库路径来查找所需的动态库。

四、注意事项

  • 库命名规则
    静态库通常以 .a 结尾,动态库通常以 .so 结尾(在 Linux 系统中)。库的名称通常遵循 lib<库名>.<后缀> 的格式,例如 libexample.a 和 libexample.so
  • 性能和空间权衡
    • 动态库
      • 优点:多个程序可以共享同一个动态库,节省磁盘空间和内存,更新库时不需要重新编译程序。
      • 缺点:可能存在兼容性问题,如果库版本不兼容,可能导致程序无法正常运行。
    • 静态库
      • 优点:可执行程序不依赖于外部库,可在不同环境中稳定运行。
      • 缺点:可执行程序体积较大,每个程序都包含一份库的副本。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/957923.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

CrypTen——基于pytorch的隐私保护机器学习框架

目录 一、CrypTen概述 二、应用场景 三、CrypTen优势 四、CrypTen技术解析 1.基于pytorch的构建基础 2.核心密码学原语 3.加密模型训练流程 五、传统隐私保护技术与CrypTen的对比 1.传统隐私保护技术介绍 2.CrypTen与传统隐私保护技术的区别 六、CrypTen的环境配置…

他把智能科技引入现代农业领域

江苏田倍丰农业科技有限公司&#xff08;以下简称“田倍丰”&#xff09;是一家专注于粮油种植的农业科技公司&#xff0c;为拥有300亩以上田地的大户提供全面的解决方案。田倍丰通过与当地政府合作&#xff0c;将土地承包给大户&#xff0c;并提供农资和技术&#xff0c;实现利…

电池预测 | 第22讲 基于GRU-Attention的锂电池剩余寿命预测

电池预测 | 第22讲 基于GRU-Attention的锂电池剩余寿命预测 目录 电池预测 | 第22讲 基于GRU-Attention的锂电池剩余寿命预测预测效果基本描述程序设计参考资料 预测效果 基本描述 电池预测 | 第22讲 基于GRU-Attention的锂电池剩余寿命预测 锂电池作为现代电子设备的重要动力…

Spring Boot AOP实现动态数据脱敏

依赖&配置 <!-- Spring Boot AOP起步依赖 --> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId> </dependency>/*** Author: 说淑人* Date: 2025/1/18 23:03* Desc…

UE虚幻引擎No Google Play Store Key:No OBB found报错如何处理?

问题描述&#xff1a; UE成功打包APK并安装过后&#xff0c;启动应用时提示&#xff1a; No Google Play Store KeyNo OBB found and no store key to try to download. Please setone up in Android Project SettingsUE配置默认在打包APK时会附加生成一个OBB文件&#xff0c;…

Github 2025-01-20 开源项目周报 Top15

根据Github Trendings的统计,本周(2025-01-20统计)共有15个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Python项目10Rust项目2TypeScript项目1C++项目1Jupyter Notebook项目1Go项目1Tabby: 自托管的AI编码助手 创建周期:310 天开发语言:Rust协议类…

给工作流穿上漂亮的衣服,创建用户交互界面并调用工作流,可定制的工作流,奶奶都能看明白的扣子智能体免费系列教程(10)

创建工作流看这个 搭建小红书梗图、歪理生成器工作流搭建 效果 欢迎来到滔滔讲AI。本教程为免费系列教程&#xff0c;感谢关注&#xff0c;以防找不到。 一、新建应用 方式一&#xff0c;点击左侧导航的加号 方式二、在工作空间面板选择右上角的创建按钮 选择创建应用 二、…

Python网络自动化运维---用户交互模块

文章目录 目录 文章目录 前言 实验环境准备 一.input函数 代码分段解析 二.getpass模块 前言 在前面的SSH模块章节中&#xff0c;我们都是将提供SSH服务的设备的账户/密码直接写入到python代码中&#xff0c;这样很容易导致账户/密码泄露&#xff0c;而使用Python中的用户交…

免费送源码:Java+springboot+MySQL 婴儿疫苗接种管理系统的设计与实现 计算机毕业设计原创定制

目 录 摘要 1 1 绪论 1 1.1概述 1 1.2课题意义 1 1.3研究思路及创新之处 1 1.4springboot框架介绍 1 1.5论文结构与章节安排 1 2 婴儿疫苗接种管理系统分析 3 2.1 可行性分析 3 2.1.1 技术可行性分析 3 2.1.2 经济可行性分析 3 2.1.3 法律可行性分析 3 2.2 系统功…

三相电变为家庭220V,市电火线和零线关系,为什么用三相电输送

参考&#xff1a; https://www.zhihu.com/question/30555841/answer/85723024 上面是电力系统的主要组成&#xff0c;发电站发电后升压传输&#xff0c;然后到各大城市再降压使用。 我们看到电塔上都是三根线&#xff0c;那么因为整个过程都是三相电。 为什么用三相电&#xff…

CIMRTS材质美化--放大采样、缩小采样

最新的CIMRTS v1.0.10中在要素管理中的材质美化增加「放大采样」和「缩小采样」参数&#xff0c;对于透明树叶可以达到较好效果。 在CesiumLab中&#xff0c;一棵树处理完成后&#xff0c;在EarthSDK中&#xff0c;就是呈现这样缩小就会有树叶丢失的情况。效果如下&#xff1a…

SQL-leetcode—1179. 重新格式化部门表

1179. 重新格式化部门表 表 Department&#xff1a; ---------------------- | Column Name | Type | ---------------------- | id | int | | revenue | int | | month | varchar | ---------------------- 在 SQL 中&#xff0c;(id, month) 是表的联合主键。 这个表格有关…

城市生命线安全保障:技术应用与策略创新

城市生命线工程是维系城市正常运行、满足群众生产生活需要的重要基础设施。随着城市化进程的加快&#xff0c;城市基础设施生命线安全运行的复杂性日益加剧&#xff0c;保障城市居民日常生活正常运行的水、电、气、热等各类地下管线以及桥梁、市政设施、轨道交通等城市基础设施…

vue2的$el.querySelector在vue3中怎么写

这个也属于直接操作 dom 了&#xff0c;不建议在项目中这样操作&#xff0c;不过我是在vue2升级vue3的时候遇到的&#xff0c;是以前同事写的代码&#xff0c;也没办法 先来看一下对比 在vue2中获取实例是直接通过 this.$refs.xxx 获取绑定属性 refxxx 的实例&#xff0c;并且…

Genetic Prompt Search via Exploiting Language Model Probabilities

题目 利用语言模型概率的遗传提示搜索 论文地址&#xff1a;https://www.ijcai.org/proceedings/2023/0588.pdf 项目地址&#xff1a;https://github.com/zjjhit/gap3 摘要 针对大规模预训练语言模型(PLMs)的即时调优已经显示出显著的潜力&#xff0c;尤其是在诸如fewshot学习…

如何引导LabVIEW项目相关方合理参与项目?

引导 LabVIEW 项目相关方合理参与项目&#xff0c;是保障项目顺利推进的关键所在。合理引导&#xff0c;既能显著提升项目执行效率&#xff0c;又能让各方清晰知晓自身在项目中的角色与责任。以下为具体策略与建议&#xff1a; ​ 一、明确项目目标与需求 清晰沟通项目目标&a…

Maven的下载安装配置

maven的下载安装配置 maven是什么 Maven 是一个用于 Java 平台的 自动化构建工具&#xff0c;由 Apache 组织提供。它不仅可以用作包管理&#xff0c;还支持项目的开发、打包、测试及部署等一系列行为 Maven的核心功能 项目构建生命周期管理&#xff1a;Maven定义了项目构建…

光谱相机在智能冰箱的应用原理与优势

食品新鲜度检测 详细可点击查看汇能感知团队实验报告&#xff1a;高光谱成像技术检测食物新鲜度 检测原理&#xff1a;不同新鲜程度的食品&#xff0c;其化学成分和结构会有所不同&#xff0c;在光谱下的反射、吸收等特性也存在差异。例如新鲜肉类和蔬菜中的水分、蛋白质、叶…

MySQL可直接使用的查询表的列信息

文章目录 背景实现方案模板SQL如何查询列如何转大写如何获取字符位置如何拼接字段 SQL适用场景 背景 最近产品找来&#xff0c;想让帮忙出下表的信息&#xff0c;字段驼峰展示&#xff0c;每张表信息show create table全部展示&#xff0c;再逐个粘贴&#xff0c;有点太耗费时…

设计模式的艺术-享元模式

结构性模式的名称、定义、学习难度和使用频率如下表所示&#xff1a; 1.如何理解享元模式 当一个软件系统在运行时产生的对象数量太多&#xff0c;将导致运行代价过高&#xff0c;带来系统性能下降等问题。 在享元模式中&#xff0c;存储这些共享实例对象的地方称为享元池&…