动静态库:选择与应用的全方位指南

目录

1 软链接

1.1 软链接的建立方式和观察现象

1.2 软链接的原理

2 硬链接

2.1 硬链接的建立方式和观察现象

2.2 硬链接的本质

2.3 我们用户不能给目录建立硬链接

3. 动静态库复习 

4 动静态库的制作

4.1 静态库的制作与使用

4.1.2 打包

4.1.3 静态库的使用

4.2 动态库的制作与使用

4.2.1  打包

4.2.2 使用 

4.3 动态库链接不到的四种解决方法

1 库安装

2 软链接

3  /etc/ld.so.conf.d配置文件

4 LD_LIBRARY_PATH环境变量

5 动静态库链接的选择

6 理解动态加载

6.1 在操作系统的角度去理解

6.2 编址

6.3 一般程序的加载问题(也就是静态库)

6.4 理解动静态库动态链接和加载问题 


1 软链接

1.1 软链接的建立方式和观察现象

ln -s 被软链接文件 软链接文件

我们在观察下面的情况,就是软链接文件,是有自己独立的inode编号的,说明他是一个独立的文件。 


1.2 软链接的原理

软链接本质上是一个独立的文件,软链接文件内容里面放的是链接文件的路径。

类似于Windows下的快捷方式。


2 硬链接

2.1 硬链接的建立方式和观察现象

ln 被硬链接文件 硬链接文件

我们观察下面,是可以发现hello.txt 和link.hard 其实上inode是一致的,这就说明link.hard其实上本质不是一个独立的文件 


2.2 硬链接的本质

既然没有新建文件,那么就是一个新的文件名而已,在目录中插入了一段新的映射关系。

那么这样inode中一定有一个引用计数的变量用于记录这个inode编号有多少段映射关系。

那么硬链接的本质就是 在目录中插入了一段新的映射关系,并且让inode结构体中的引用计数++。

我们可以看到,这个查看文件信息是的这个文字我们从来没有提到过。

这个其实上就是文件的硬链接数:表明这个文件有多少个硬链接

我们回到上机目录,我们发现我们刚刚所处的目录居然有两个硬链接,但是,我们并没有给他创建硬链接啊,我们仔细对比了inode编号 目录的编号是于目录内的隐藏文件./是一致的。

所以我们得出结论,我们经常使用的 .. 和 . 其实就是目录的硬链接。


2.3 我们用户不能给目录建立硬链接

主要是因为,这样我们在查找文件的时候会陷入环路问题。 

注:硬链接无法跨分区,因为只有在分区里面inode才唯一。


3. 动静态库复习 

我们在编写代码的时候都使用过库。

动态库:libXXX.so  静态库:libYYY.a(库的真是名字其实是XXX和YYY那部分)

在Linux环境下,gcc默认链接的都是动态库,云服务器上其实连静态库都没有安装。

如果,要编译链接静态库,需要在后面加上 -static


4 动静态库的制作

动静态库的本质其实上就是一大堆的可执行程序。

将这些经过编译的二进制文件打包,这样就形成了库。

库的意义:这样隐藏了源代码,又提高了生产效率


4.1 静态库的制作与使用


4.1.2 打包

ar [options] archive-file object-files

指令解析:

  1. archive-file是静态库名(lib库名.a);object-files是要添加到库中的对象名(.o文件)。

  2. 常见的选项

-c:创建库文件,如果库已存在,则会被覆盖。

-r:向库文件中添加.o文件,如果.o文件已在库中存在,则会被替换。

-t:列出库文件中包含的.o文件列表。

-v:在执行过程中显示详细的信息。

第一步:编译形成 .o 文件。

第二步:使用ar命令,将所有.o文件进行打包,形成静态库文件。

第三步:将库进行标准化。

libmyc.a:my_add.o my_sub.o  //第二步:使用ar命令,将所有.o文件进行打包,形成静态库文件
    ar -rc $@ $^

%.o:%.c  //第一步:编译形成 .o 文件
    gcc -c $<
 
.PHONY:clean  
clean:
    rm -rf *.a *.o mylib mylib.tgz
  
.PHONY:output  //第三步:将库进行标准化
output:   
    mkdir -p mylib/include
    mkdir -p mylib/lib 
    cp -rf *.h mylib/include/
    cp -rf *.a mylib/lib/
    tar czf mylib.tgz mylib   


4.1.3 静态库的使用

1.-I(i的大写):用来指定编译器搜索头文件的额外路径。
当编译器在编译过程中遇到#include指令时,它先会在标准的位置(当前目录或系统默认的头文件路径)来查找指定的头文件,如果查找不到,编译器就会使用-I指定的路径进行搜索。

2.-L:用来指定链接器搜索库文件的额外路径。
当链接器在链接中需要找到某个库文件(.so、.a),它先会在标准的位置(系统默认的库路径)中查找,如果查找不到,链接器就会使用-L指定的路径进行搜索。

3.-l(L的小写):用来指定链接器在链接过程中要链接的库。
补充:头文件的搜索路径:当前目录、系统默认的头文件路径(/usr/include、/usr/local/include)、gcc内置的标准头文件路径、命令行中通过-l选项指定的头文件路径。 库文件的搜索路径:系统默认的库文件路径(/usr/lib、/usr/local/lib)、gcc内置的库文件路径、命令行中通过-L选项指定的库文件路径、环境变量LIBARY_PATH中指定的路径。

这些选项分别用于控制编译和链接过程中的头文件、库文件的搜索路径和库文件的选择。


4.2 动态库的制作与使用

动态库制作的过程中只需要使用gcc就行了

4.2.1  打包

第一步:使用-fPIC选项,编译形成 .o 文件。

fPIC:产生位置无关码(position independent code)

  • -fPIC:用于指示编译器生成与位置无关的代码,无论代码被加载到内存的哪个位置,它都能正确运行,而不依赖于它在编译或加载时的具体地址。这种特性通过使用相对寻址,而不是绝对寻址来实现的。这对于创建共享库是至关重要的,因为共享库可以在进程地址空间的任何位置被加载。

第二步:使用-shared,将所有.o文件进行打包,形成动态库文件。

  • -shared:告诉编译器gcc生成一个共享库(.so或.dll文件)。

第三步:将库进行标准化

libmyc.so:my_add.o my_sub.o  //第二步:使用-shared,将所有.o文件进行打包,形成动态库文件
    gcc -shared -o $@ $^

%.o:%.c  //第一步:使用-fPIC选项,编译形成 .o 文件
    gcc -c -fPIC $<
 
.PHONY:clean  
clean:
    rm -rf *.so *.o mylib mylib.tgz
  
.PHONY:output  //第三步:将库进行标准化
output:   
    mkdir -p mylib/include
    mkdir -p mylib/lib 
    cp -rf *.h mylib/include/
    cp -rf *.so mylib/lib/
    tar czf mylib.tgz mylib   


4.2.2 使用 

动态库,再被使用的时候,是要在任何时候都要保证能被链接到的。

这个情况,就是在编译的时候,指定了路径去链接数据库。

但是,在执行程序的时候,由于没指明数据库,数据库也不再默认路径下,所以,就找不到。


4.3 动态库链接不到的四种解决方法

1 库安装

  1. 将库或其他软件安装到系统中,本质是把对应的文件,拷贝到系统默认的搜索路径中。

  2. 在64位系统,系统中库默认的搜索路径为/lib64、/usr/lib64;在32系统,系统中库默认的搜索路径为/lib、/usr/lib。

  3. 但是呢,这个方法是建议将别人写的成熟的库进行拷贝,如果只是自己写的,仅仅用于测试的简易的库,不建议直接拷贝进去

 

拷贝进去之后,这个文件立马就能运行了。
你也发现了,我一运行完就删除了,就是为了防止这种不成熟的库,污染我的库。


2 软链接

 

ldd Filename 可以查看文件所链接的库 


3  /etc/ld.so.conf.d配置文件

  1. /etc/ld.so.conf.d目录下的配置文件,用来指定额外的库文件的搜索路径,以便动态链接器能够在运行时找到并加载这些库文件。


4 LD_LIBRARY_PATH环境变量

LD_LIBRARY_PATH:是一个环境变量,在linux中,为动态链接器指定额外的库搜索路径


5 动静态库链接的选择

  1. 如果,静态库和动态库我们都提供了,那么编译器默认会选择的是静态链接
  2. 如果非要使用静态链接,那么就使用static选项
  3. 如果我们只提供了静态库,那么编译器也没用办法,只能对这个库使用静态链接,但是整个程序不一定全是静态链接。
  4. 如果我们只提供动态库,那么gcc默认动态链接,如果非要静态链接,那么就会链接报错。 


6 理解动态加载

6.1 在操作系统的角度去理解

一、动态库概念

动态库:也称为共享库,是一种包含代码和数据,可以在多个程序之间共享的文件,存放在磁盘上。


与静态库不同,静态库在程序编译时会被完全复制到可执行文件中,而共享库则在程序运行时被加载到内存中,如果多个程序使用同一个共享库,OS会让这些进程共享内存中的同一份库代码和数据,即:动态库的代码和数据在内存中只存在一份。

管理:系统中可以同时存在多个已经被加载的库,OS需要管理它们,先描述(包含了加载地址等信息)、再组织。

二、动态库加载的过程

检查依赖:程序启动时,动态链接器会检查该程序依赖的所有动态库。

搜索路径:动态链接器会在预设的库搜索路径中查找所需的动态库文件。

加载与映射:第一次加载、后续加载。

第一次加载:如果动态库尚未被加载到内存中,动态链接器会将该库加载到内存中,并映射到进程地址空间的共享区中。

后续加载:如果其他进程也需要共享这个库,动态链接器会检查内存中是否已存在该库;如果已存在,只需修改地址空间中共享区的映射关系,指向已存在的库副本;如果不存在,则重复第一次加载的过程。

优点:节省内存、易于更新、提高了程序的性能和安全性。


6.2 编址

其实上就是谈谈可执行程序的问题。

第一个问题:一开始我们的程序在没有加载进入内存的时候有没有地址? ---  有了

其实,我们的程序在没有加载进入内存的时候,根据类别(比如权限,访问属性等),把可执行程序划分成了几个区域 。

  我们之前提到的进程地址空间,其实上本质就是一个结构体(mm_struct),我们结构体里面记载了各个区域的开始地址和结束地址(就是两个指针变量),利用这种双指针的记载方式,我们达成了分区的效果。

那么这就有一个问题?这个结构体(即进程地址空间结构体)是由谁来初始化的?每个结构体的代码大小都不同,那么正文段的大小也应该不同,结构体的数据怎么来?

 ----  里面的数据很多都是从可执行程序中来的 (和操作系统,编译器都有关系)

编址:在编译和链接阶段,为程序和库中的符号(变量、函数)分配地址的过程,主要有绝对编址、相对编址两种方式。

可编址的范围:32位平台,[0, 2^32] -> [0, 4GB] ,64位平台,[0, 2^64] -> [0, 16GB]。

绝对编址:在编译和链接过程中,符号的地址是固定的,即:已经确定了符号的实际的物理内存地址。这种方式要求程序运行时,必须加载到特定的物理地址处,否则无法正确的运行。
绝对编址中的地址 == 实际的物理内存地址。

相对编制:也成为逻辑地址、虚拟地址。在编译和链接过程中,符号的地址是不固定的,而是相对于某个基地址的偏移量。这种方式允许程序在加载时动态确定实际地址,从而实现位置无关代码。
符号地址 = 基地址 + 偏移量。基地址在编译链接阶段是未知的,通常是由OS在程序加载时分配的虚拟地址,是在地址空间内的一个起始地址,如:0x400000。

回答前面提到的问题:地址空间、页表中的数据来自哪里?

  • 那不就是直接用代码编址编好的地址,直接用作虚拟地址,来填充页表。 

所以这里也提出一个观点:虚拟地址空间不仅仅OS系统要遵守,编译器也要遵守。 

目前,使用的都是平坦模式下编址,为了兼容前面的相对编址,可以将全部代码看做一大块空间,那么,绝对编址下的代码的地址就是:0 + 偏移量。 

每个可执行程序大小不同,说明了每个程序中各个区域虚拟地址范围也会不同。相应地,当这些程序被加载到内存变为进程时,则每个进程地址空间中各个区域的虚拟地址的范围也是不同的。

6.3 一般程序的加载问题(也就是静态库)

静态库,本身就是拷贝进入代码的,就按照普通代码,进行编址,然后加载到内存中,把编译出来的绝对地址,当做虚拟地址,进行页表映射。

一.先加载形成PCB和mm_struct,再对他们进行初始化

  1. 我们先明白一个点:在程序加载进入内存的时候是先加载进入代码还是先形成PCB ---PCB
  2. 就不说堆栈和共享区都是动态开辟的,在加载进入内存的时候开辟的,那么那些正文代码,初始化数据和未初始化数据,每个程序都是不同的,那么这个如何在程序未加载进入内存的时候,如何进行初始化呢? ---- 利用:可执行程序的一块特殊的文件区域,来进行对mm_struct进行初始化

二.再将整个可执行程序加载进入内存当中,在构建页表

代码也是数据,我们将代码加载进入内存的时候,那此时,这条代码既有了在代码编译时形成逻辑地址,也有了在内存上的物理地址。那么此时也就有了虚拟地址和物理地址的相互对应,这样就可以构建页表了

问:那么CPU如何找到程序的入口地址,然后运行程序的过程

用可执行程序初始化mm_struct的时候,就会顺便把头文件里的main函数的虚拟地址加载进入CPU的PC指令当中,再利用MMU和页表配合,就可以完成虚拟到物理的转化,这样就找到了程序入口。

然后,将指令读取到CPU中的指令寄存器中,指令:因为要调用函数,所以再将0x100000进行虚拟到物理的转化,找到函数,再执行函数

所以我们在这里输出一个结论:进程地址空间是一个由操作系统(创建PCB和mm_struct)+ 编译器(编译代码和形成逻辑地址)+ 计算机体系结构(虚拟地址到物理地址的转化)三者合作形成的概念。

问题:CPU如何对各式各样的指令,来做出正确的反应呢?

CPU其实就只能识别二进制,我们将所有的代码翻译成二进制文件的时候,CPU内部有一个指令集,里面记载了对于各式各样的二进制指令,CPU该如何去进行对应的动作。所以,将我们传入的指令与指令集相对应,然后做出反应

6.4 理解动静态库动态链接和加载问题 

库函数和代码都加载进入内存的时候,调用库函数的方法的逻辑地址都是一致的
在编译器编译代码内部的动态库方法,并不会赋予它一个新的逻辑地址,而是会沿用动态 库内部这个方法的逻辑地址

我们知道,动态库的代码在虚拟地址映射的时候是映射到共享区的,所以在正文代码在执行的时候,执行到了动态库中的方法,此时,就要跳转到共享区,利用虚拟地址( start+偏移量)找到该方法,在共享区的位置,然后进行虚拟地址到物理地址的转化,找到该方法在内存上的位置,读取到CPU内部,开始执行代码

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

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

相关文章

【ROS2】多传感器融合、实现精准定位:robot_localization

1、简述 robot_localization在SLAM建图、导航中常用于将多个传感器融合(IMU、里程计、深度相机、GPS等),以提高定位精度,为机器人提供了在三维空间中的非线性状态估计 robot_localization包含两个状态估计节点: ekf_localization_node:扩展卡尔曼滤波(EKF),缺点是非…

极客大挑战2024wp

极客大挑战2024wp web 和misc 都没咋做出来&#xff0c;全靠pwn✌带飞 排名 密码学和re没做出几个&#xff0c;就不发了 web ez_pop 源代码 <?php Class SYC{public $starven;public function __call($name, $arguments){if(preg_match(/%|iconv|UCS|UTF|rot|quoted…

40分钟学 Go 语言高并发:并发下载器开发实战教程

并发下载器开发实战教程 一、系统设计概述 1.1 功能需求表 功能模块描述技术要点分片下载将大文件分成多个小块并发下载goroutine池、分片算法断点续传支持下载中断后继续下载文件指针定位、临时文件管理进度显示实时显示下载进度和速度进度计算、速度统计错误处理处理下载过…

李宏毅机器学习课程知识点摘要(1-5集)

前5集 过拟合&#xff1a; 参数太多&#xff0c;导致把数据集刻画的太完整。而一旦测试集和数据集的关联不大&#xff0c;那么预测效果还不如模糊一点的模型 所以找的数据集的量以及准确性也会影响 由于线性函数的拟合一般般&#xff0c;所以用一组函数去分段来拟合 sigmoi…

Spring Boot教程之五:在 IntelliJ IDEA 中运行第一个 Spring Boot 应用程序

在 IntelliJ IDEA 中运行第一个 Spring Boot 应用程序 IntelliJ IDEA 是一个用 Java 编写的集成开发环境 (IDE)。它用于开发计算机软件。此 IDE 由 Jetbrains 开发&#xff0c;提供 Apache 2 许可社区版和商业版。它是一种智能的上下文感知 IDE&#xff0c;可用于在各种应用程序…

本地Docker部署开源WAF雷池并实现异地远程登录管理界面

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

如何快速将Excel数据导入到SQL Server数据库

工作中&#xff0c;我们经常需要将Excel数据导入到数据库&#xff0c;但是对于数据库小白来说&#xff0c;这可能并非易事&#xff1b;对于数据库专家来说&#xff0c;这又可能非常繁琐。 这篇文章将介绍如何帮助您快速的将Excel数据导入到sql server数据库。 准备工作 这里&…

[产品管理-91]:产品经理的企业运营的全局思维-1

目录 前言&#xff1a;企业架构图 产品经理的企业运营全局思维 1、用户 - 用户价值与体验&#xff1a;真正的需求&#xff0c;真正的问题&#xff0c;一切的原点 2、大势 - 顺应宏观大势&#xff1a;政策趋势、行业趋势、技术趋势 3、市场 - 知己知彼&#xff1a;市场调研…

简单实现vue2响应式原理

vue2 在实现响应式时&#xff0c;是根据 object.defineProperty() 这个实现的&#xff0c;vue3 是通过 Proxy 对象实现&#xff0c;但是实现思路是差不多的&#xff0c;响应式其实就是让 函数和数据产生关联&#xff0c;在我们对数据进行修改的时候&#xff0c;可以执行相关的副…

论文解析:EdgeToll:基于区块链的异构公共共享收费系统(2019,IEEE INFOCOM 会议);layer2 应对:频繁小额交易,无交易费

目录 论文解析:EdgeToll:基于区块链的异构公共共享收费系统(2019,IEEE INFOCOM 会议) 核心内容概述 核心创新点原理与理论 layer2 应对:频繁小额交易,无交易费 论文解析:EdgeToll:基于区块链的异构公共共享收费系统(2019,IEEE INFOCOM 会议) 核心内容是介绍了一个…

基于python Django的boss直聘数据采集与分析预测系统,爬虫可以在线采集,实时动态显示爬取数据,预测基于技能匹配的预测模型

本系统是基于Python Django框架构建的“Boss直聘”数据采集与分析预测系统&#xff0c;旨在通过技能匹配的方式对招聘信息进行分析与预测&#xff0c;帮助求职者根据自身技能找到最合适的职位&#xff0c;同时为招聘方提供更精准的候选人推荐。系统的核心预测模型基于职位需求技…

SemiDrive E3 硬件设计系列---唤醒电路设计

一、前言 E3 系列芯片是芯驰半导体高功能安全的车规级 MCU&#xff0c;对于 MCU 的硬件设计部分&#xff0c;本系列将会分模块进行讲解&#xff0c;旨在介绍 E3 系列芯片在硬件设计方面的注意事项与经验&#xff0c;本文主要讲解 E3 硬件设计中唤醒电路部分的设计。 二、RTC 模…

Leetcode198. 打家劫舍(HOT100)

代码&#xff1a; class Solution { public:int rob(vector<int>& nums) {int n nums.size();vector<int> f(n 1), g(n 1);for (int i 1; i < n; i) {f[i] g[i - 1] nums[i - 1];g[i] max(f[i - 1], g[i - 1]);}return max(f[n], g[n]);} }; 这种求…

一文探究48V新型电气架构下的汽车连接器

【哔哥哔特导读】汽车电源架构不断升级趋势下&#xff0c;48V系统是否还有升级的必要&#xff1f;48V新型电气架构将给连接器带来什么改变&#xff1f; 在插混和纯电车型逐渐普及、800V高压平台持续升级的当下&#xff0c;48V技术还有市场吗? 这个问题很多企业的回答是不一定…

React学习05 - redux

文章目录 redux工作流程redux理解redux理解及三个核心概念redux核心apiredux异步编程react-redux组件间数据共享 纯函数redux调试工具项目打包 redux工作流程 redux理解 redux是一个专门用于状态管理的JS库&#xff0c;可以用在react, angular, vue 等项目中。在与react配合使…

2024年11月最新 Alfred 5 Powerpack (MACOS)下载

在现代数字化办公中&#xff0c;我们常常被繁杂的任务所包围&#xff0c;而时间的高效利用成为一项核心需求。Alfred 5 Powerpack 是一款专为 macOS 用户打造的高效工作流工具&#xff0c;以其强大的定制化功能和流畅的用户体验&#xff0c;成为众多效率爱好者的首选。 点击链…

batchnorm与layernorn的区别

1 原理 简单总结&#xff1a; batchnorn 和layernorm是在不同维度上对特征进行归一化处理。 batchnorm在batch这一维度上&#xff0c; 对一个batch内部所有样本&#xff0c; 在同一个特征通道上进行归一化。 举个例子&#xff0c; 假设输入的特征图尺寸为16x224x224x256&…

【c++丨STL】stack和queue的使用及模拟实现

&#x1f31f;&#x1f31f;作者主页&#xff1a;ephemerals__ &#x1f31f;&#x1f31f;所属专栏&#xff1a;C、STL 目录 前言 一、什么是容器适配器 二、stack的使用及模拟实现 1. stack的使用 empty size top push和pop swap 2. stack的模拟实现 三、queue的…

ApiChain 从迭代到项目 接口调试到文档生成单元测试一体化工具

项目地址&#xff1a;ApiChain 项目主页 ApiChain 简介 ApiChain 是一款类似 PostMan 的接口网络请求与文档生成软件&#xff0c;与 PostMan 不同的是&#xff0c;它基于 项目和迭代两个视角管理我们的接口文档&#xff0c;前端和测试更关注版本迭代中发生变更的接口编写代码…

力扣面试题 - 24 插入

题目&#xff1a; 给定两个整型数字 N 与 M&#xff0c;以及表示比特位置的 i 与 j&#xff08;i < j&#xff0c;且从 0 位开始计算&#xff09;。 编写一种方法&#xff0c;使 M 对应的二进制数字插入 N 对应的二进制数字的第 i ~ j 位区域&#xff0c;不足之处用 0 补齐…