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

Linux静态库与动态库加载

了解库:

        关于库相比大家之前肯定使用过,比如C/C++里面的标准库,STL里面的各种库,我们在调用STL里的容器时都需要使用库,那么库到底是什么呢?

库的本质就是可执行程序的"半成品"

我们先来回顾一下代码的执行流程:

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

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

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

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

现在我们有这样的场景,有test1.c,test2.c,test3.c 最终经过上述的一系列处理转化成了test1.o,test2.o,test3.o,然后将这3个文件链接形成一个可执行程序a.out:

现在我想把我写的这个功能分享给别人,让别人能够使用test*.c中的方法,但是我不想让别人看到.c中的源代码,所以我们就可以,将test*.o打包成库,然后将库和头文件(方法的使用手册),发给别人,别人拿到库和头文件,查看头文件里每个方法的使用进行使用.o里的方法。

实际上,库的本质是一堆目标文件的集合(xxx.o)的集合,里面没用main函数但存在很多课调用的方法。

认识动静态库:

        我们在liunx下见一见库吧,现在我创建test1.c,编写如下程序:

这是一个非常简单的c语言程序,编译运行一下:

注意我们调用了printf函数,但我们并没有写printf函数的实现,为什么能输出结果呢?主要原因是编译器将c语言的c标准库链接进来了,c标准库里已经写好了printf的函数实现。

查看文件链接的标准库:ldd

这就是链接的c标准库,我们查一下这个文件libc.so.6:

没错,libc.so.6是一个软链接文件,我们再来查看一下这个目标文件的文件类型,使用file命令:

 可以发现它是一个共享的库,简单来说就是动态库。

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

认识了动态库,那静态库,又是什么呢?

动态库是和目标文件链接,具体怎么链接下面会讲,而静态库确不同,静态库是在编译的时候,将库中的代码直接拷贝到目标文件中,这就导致了我们最终形成的目标文件会很大,但优势在于形成了可执行程序后,该可执行程序可独立运行,不再需要库,但动态库不行,在日常我们都会使用动态库,很少使用静态库。

写一个自己的库:

在认识完库后,我们其实现在已经完全可以自己写一个库,我们简单实现一下加法:

test2.c:

test3.c:

test2.h:

test3.h:

我们先编程test* .c 形成test*.o:

 使用ar命令将目标文件打包成静态库,下面我们使用ar命令的-r选项和-c选项进行打包。

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

再打包一个静态库,改后缀即可:

 还可以使用ar命令中的选项查看库中的文件的信息:

  • -t:列出库中的文件。
  • -v(verbose):显示详细的信息。

当我们把库给别人使用时,只需两个文件,一个存放头文件,一个存放库,所以我将所以头文件放入include目录下,两个库放在lib下,再将这两个目录放在mylib下:

 使用:

现在我们就相当于这个库的使用者,我们编写main函数来使用Add和Sub函数:

这里我们不能直接用gcc像以前一样编译main.c,因为现在gcc编译器默认只认系统提供的库,而我们需要链接的是第三方库,这里我们链接第三方库有这几种方法如下:

第一种方法我们给编译器加上一些选项(推荐):

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

注意-l后面接库名时需要去掉库的前缀lib和库的后缀.so或者.a

gcc main.c -I ./mylib/include/ -L ./mylib/lib/ -lmyc

 运行一下:

第二种方法:将头文件和库拷贝到系统默认的库路径

前提我们需要知道系统默认的头文件和库文件路径:

系统头文件: /usr/include

系统库文件: /usr/lib64

将我们的库和头文件拷贝到对应目录就行,这里不做演示,因为这种方法不是很推荐,因为我们应尽量避免对系统默认文件修改。 

注意上面我打包了两个库,虽然看后缀一个静态库,一个动态库,但是其实上述的库都是静态库,linux不是单单通过后缀来判断一个库的类型,下面就来看看如何真正打包一个动态库吧。

动态库打包:

还在之前的几个目标文件:

用gcc编译时带上-fPIC选项:

  • -fPIC(position independent code):产生位置无关码。
gcc -fPIC -c test2.c test3.c

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

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

与生成静态库不同的是,生成动态库时我们不必使用ar命令,我们只需使用gcc的-shared选项即可。

gcc -shared -o libmyc.so test2.o test3.o

 像之前一样将头文件和动态库组合进一个文件夹:

该动态库和刚才的静态库使用方法一样:

gcc main.c -o a.out -I ./libmyc/include/ -L ./libmyc/lib/ -lmyc

需要注意的是,我们使用-I-L-l这三个选项都是在编译期间告诉编译器我们使用的头文件和库文件在哪里以及是谁,但是当生成的可执行程序生成后就与编译器没有关系了,此后该可执行程序运行起来后,操作系统找不到该可执行程序所依赖的动态库,我们可以使用ldd命令进行查看。 

我们只是告诉了编译器头文件和动态库的位置,编程成可执行程序运行后变成进程,就和编译器无关了,就变成了一个进程,进程被操作系统管理,此时操作系统还不知道头文件和动态库的位置。

解决方法如下:

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

cp ./libmyc/lib/libmyc.so /usr/lib64/

注意系统共享库路径,系统不同可能路径名有差异,需自行百度 

 这时我们再运行a.out:

方法二:更改LD_LIBRARY_PATH

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

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/testdir/libmyc/lib

此方法在ubuntu 20.4版本已失效,故不作演示,centos的小伙伴可以自己去试一下。

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

contos: 

/etc/ld.so.conf.d/路径下存放的全部都是以.conf为后缀的配置文件,而这些配置文件当中存放的都是路径,系统会自动在/etc/ld.so.conf.d/路径下找所有配置文件里面的路径,之后就会在每个路径下查找你所需要的库。所以我们只需将动态库路径放在该配置文件中,当程序执行后,系统就会通过该配置文件找到所需的库。

先来看看/etc/ld.so.conf.d/

首先将库文件所在目录的路径存入一个以.conf为后缀的文件当中。

再将这个.conf文件放入/etc/ld.so.conf.d中:

此时a.out还是无法链接动态库的,因为配置文件只在系统刚启动的时候会更新生效给我们的系统配置好,中途修改无法马上修改,所以我们用ldconfig命令更新一下配置文件:

ldconfig

 最终可运行a.out,经过我的实测,unbuntu貌似无法使用该方法,不过unbuntu还有别的方法

unbuntu:

在/etc/ld.so.conf后面直接追加新第三方动态库路径:

nano /etc/ld.so.conf

 同样改完后ldconfig使配置文件生效。最后运行a.out

 

 

 

相关文章:

Linux静态库与动态库加载

了解库: 关于库相比大家之前肯定使用过,比如C/C里面的标准库,STL里面的各种库,我们在调用STL里的容器时都需要使用库,那么库到底是什么呢? 库的本质就是可执行程序的"半成品" 我们先来回顾一下代…...

Whisper-AT:抗噪语音识别模型(Whisper)实现通用音频事件标记(Audio Tagger)

本文介绍一个统一音频标记(Audio Tagger)和语音识别(ASR)的模型:Whisper-AT,通过冻结Whisper的主干,并在其之上训练一个轻量级的音频标记模型。Whisper-AT在额外计算成本不到1%的情况下&#xf…...

K8s:Pod初识

Pod Pod是k8s处理的最基本单元。容器本身不会直接分配到主机上,封装为Pod对象,是由一个或多个关系紧密的容器构成。她们共享 IPC、Network、和UTS namespace pod的特征 包含多个共享IPC、Network和UTC namespace的容器,可直接通过loaclhos…...

HCIP-Datacom-ARST自选题库__MAC【14道题】

一、单选题 1.缺省情况下,以下哪种安全MAC地址类型在设备重启后表项会丢失? 黑洞MAC地址 Sticky MAC地址 安全动态MAC地址 安全静态MAC地址 2.华为交换机MAC地址表中的动态sticky MAC地址的默认老化时间是多少秒? 300 不会老化 400 500 3.华为交换机MA…...

Go基础编程 - 03 - init函数、main函数、_(下划线)

目录 1. init 函数2. main 函数3. init 函数与 main 函数异同4. _ (下划线)示例 1. init 函数 Go语言中,init 函数用于包(package)的初始化。具有以下特征: 1. init 函数用于程序执行前包的初始化,如初始化变量等。2…...

【TensorFlow深度学习】LeNet-5卷积神经网络实战分析

LeNet-5卷积神经网络实战分析 LeNet-5卷积神经网络实战分析:从经典模型到现代实践LeNet-5的历史背景LeNet-5网络架构实战代码解析实战分析结论 LeNet-5卷积神经网络实战分析:从经典模型到现代实践 在深度学习的历程中,LeNet-5无疑是一座里程…...

错误发生在尝试创建一个基于有限元方法的功能空间时

问题&#xff1a; index cell.index(#直接使用从0开始的索引if0<1ndex<10: #正集流体 subdomains_x[cell,index(] 1 fem1 /usr/bin/python3.8 /home/wy/PycharmProjects/pythonProject2/fem1.pyUnknown ufl object type FiniteElementTraceback (aost recent call last)…...

【八股】Hibernate和JPA:理解它们的关系

在Java开发中&#xff0c;持久化框架是至关重要的工具&#xff0c;它们帮助开发者将Java对象与关系数据库中的数据进行映射和管理。Hibernate和JPA&#xff08;Java Persistence API&#xff09;是两个广泛使用的持久化框架。那么&#xff0c;Hibernate和JPA之间到底是什么关系…...

C++类型参数技术以及常见的类型擦除容器

文章目录 一、类型擦除的作用二、常见的类型擦除容器1.std::any2.std::function3.std::shared_ptr\<void\>和 std::unique_ptr\<void\>4.总结 三、实现一个any参考 类型擦除&#xff08;Type Erasure&#xff09;是一种编程技术&#xff0c;通过它可以在运行时存储…...

SpringBoot如何缓存方法返回值?

Why&#xff1f; 为什么要对方法的返回值进行缓存呢&#xff1f; 简单来说是为了提升后端程序的性能和提高前端程序的访问速度。减小对db和后端应用程序的压力。 一般而言&#xff0c;缓存的内容都是不经常变化的&#xff0c;或者轻微变化对于前端应用程序是可以容忍的。 否…...

C#的web项目ASP.NET

添加实体类和控制器类 using System; using System.Collections.Generic; using System.Linq; using System.Web;namespace WebApplication1.Models {public class Company{public string companyCode { get; set; }public string companyName { get; set; }public string com…...

Spring MVC 源码分析之 DispatcherServlet#getHandlerAdapter 方法

前言&#xff1a; 前面我们分析了 Spring MVC 的工作流程源码&#xff0c;其核心是 DispatcherServlet#doDispatch 方法&#xff0c;我们前面分析了获取 Handler 的方法 DispatcherServlet#getHandler 方法&#xff0c;本篇我们重点分析一下获取当前请求的适配器 HandlerAdapt…...

假设检验学习笔记

1. 假设检验的基本概念 1.1. 原假设&#xff08;零假设&#xff09; 对总体的分布所作的假设用表示&#xff0c;并称为原假设或零假设 在总体分布类型已知的情况下&#xff0c;仅仅涉及总体分布中未知参数的统计假设&#xff0c;称为参数假设 在总体分布类型未知的情况下&#…...

vue3 watch学习

watch的侦听数据源类型 watch的第一个参数为侦听数据源&#xff0c;有4种"数据源"&#xff1a; ref&#xff08;包括计算属性&#xff09; reactive(响应式对象) getter函数 多个数据源组成的数组。 //ref const xref(0)//单个ref watch(x,(newX)>{console.…...

推荐的Pytest插件

推荐的Pytest插件 Pytest的插件生态系统非常丰富&#xff0c;以下是一些特别推荐的Pytest插件&#xff1a; pytest-sugar 这个插件改进了Pytest的默认输出&#xff0c;添加了进度条&#xff0c;并立即显示失败的测试。它不需要额外配置&#xff0c;只需安装即可享受更漂亮、更…...

C语言 | Leetcode C语言题解之第124题二叉树中的最大路径和

题目&#xff1a; 题解&#xff1a; /*** Definition for a binary tree node.* struct TreeNode {* int val;* struct TreeNode *left;* struct TreeNode *right;* };*/ int max; int dfs(struct TreeNode* root){if(!root) return 0;int left dfs(root->left…...

Linux综合实践(Ubuntu)

目录 一、配置任务 1.1 配置该服务器的软件源为中科大软件源 1.2 安装相关软件openssh-server和vim 1.3 设置双网卡&#xff0c;网卡1为NAT模式&#xff0c;网卡2为桥接模式(桥接模式下&#xff0c;使用静态ip&#xff0c;该网卡数据跟实验室主机网络设置相似&#xff0c;除…...

C++面试题其二

19. STL中unordered_map和map的区别 unordered_map 和 map 都是C标准库中的关联容器&#xff0c;但它们在实现和性能方面有显著区别&#xff1a; 底层实现&#xff1a;map 是基于红黑树实现的有序关联容器&#xff0c;而 unordered_map 是基于哈希表实现的无序关联容器。元素…...

系统架构设计师【第9章】: 软件可靠性基础知识 (核心总结)

文章目录 9.1 软件可靠性基本概念9.1.1 软件可靠性定义9.1.2 软件可靠性的定量描述9.1.3 可靠性目标9.1.4 可靠性测试的意义9.1.5 广义的可靠性测试与狭义的可靠性测试 9.2 软件可靠性建模9.2.1 影响软件可靠性的因素9.2.2 软件可靠性的建模方法9.2.3 软件的可靠性模…...

x264 参考帧管理原理:i_poc_type 变量

x264 参考帧管理 x264 是一个开源的 H.264 视频编码软件,它提供了许多高级特性,包括对参考帧的高效管理。参考帧管理是视频编码中的一个重要部分,它涉及到如何存储、更新和使用已经编码的帧以提高编码效率。 x264 参考帧管理的一些关键点总结如下: 参考帧的初始化和重排序:…...

ubuntu搭建nfs服务centos挂载访问

在Ubuntu上设置NFS服务器 在Ubuntu上&#xff0c;你可以使用apt包管理器来安装NFS服务器。打开终端并运行&#xff1a; sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享&#xff0c;例如/shared&#xff1a; sudo mkdir /shared sud…...

Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)

目录 1.TCP的连接管理机制&#xff08;1&#xff09;三次握手①握手过程②对握手过程的理解 &#xff08;2&#xff09;四次挥手&#xff08;3&#xff09;握手和挥手的触发&#xff08;4&#xff09;状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...

06 Deep learning神经网络编程基础 激活函数 --吴恩达

深度学习激活函数详解 一、核心作用 引入非线性:使神经网络可学习复杂模式控制输出范围:如Sigmoid将输出限制在(0,1)梯度传递:影响反向传播的稳定性二、常见类型及数学表达 Sigmoid σ ( x ) = 1 1 +...

【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)

1.获取 authorizationCode&#xff1a; 2.利用 authorizationCode 获取 accessToken&#xff1a;文档中心 3.获取手机&#xff1a;文档中心 4.获取昵称头像&#xff1a;文档中心 首先创建 request 若要获取手机号&#xff0c;scope必填 phone&#xff0c;permissions 必填 …...

C++使用 new 来创建动态数组

问题&#xff1a; 不能使用变量定义数组大小 原因&#xff1a; 这是因为数组在内存中是连续存储的&#xff0c;编译器需要在编译阶段就确定数组的大小&#xff0c;以便正确地分配内存空间。如果允许使用变量来定义数组的大小&#xff0c;那么编译器就无法在编译时确定数组的大…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

Git常用命令完全指南:从入门到精通

Git常用命令完全指南&#xff1a;从入门到精通 一、基础配置命令 1. 用户信息配置 # 设置全局用户名 git config --global user.name "你的名字"# 设置全局邮箱 git config --global user.email "你的邮箱example.com"# 查看所有配置 git config --list…...

DeepSeek源码深度解析 × 华为仓颉语言编程精粹——从MoE架构到全场景开发生态

前言 在人工智能技术飞速发展的今天&#xff0c;深度学习与大模型技术已成为推动行业变革的核心驱动力&#xff0c;而高效、灵活的开发工具与编程语言则为技术创新提供了重要支撑。本书以两大前沿技术领域为核心&#xff0c;系统性地呈现了两部深度技术著作的精华&#xff1a;…...

Python竞赛环境搭建全攻略

Python环境搭建竞赛技术文章大纲 竞赛背景与意义 竞赛的目的与价值Python在竞赛中的应用场景环境搭建对竞赛效率的影响 竞赛环境需求分析 常见竞赛类型&#xff08;算法、数据分析、机器学习等&#xff09;不同竞赛对Python版本及库的要求硬件与操作系统的兼容性问题 Pyth…...

多元隐函数 偏导公式

我们来推导隐函数 z z ( x , y ) z z(x, y) zz(x,y) 的偏导公式&#xff0c;给定一个隐函数关系&#xff1a; F ( x , y , z ( x , y ) ) 0 F(x, y, z(x, y)) 0 F(x,y,z(x,y))0 &#x1f9e0; 目标&#xff1a; 求 ∂ z ∂ x \frac{\partial z}{\partial x} ∂x∂z​、 …...