【Linux】软硬链接与动静态库

系列文章

收录于【Linux】文件系统 专栏

关于文件描述符与文件重定向的相关内容可以移步 文件描述符与重定向操作。

可以到 浅谈文件原理与操作 了解文件操作的系统接口。

想进一步理解文件系统还可以看看文件缓冲区和文件系统。


目录

系列文章

软硬链接

软链接

硬链接

文件时间

库的介绍

静态库

打包

使用

动态库

打包

使用

原理


软硬链接

软链接

🌟在 Linux 下有两种链接方法可以帮助我们更快地访问到存于某个位置的文件,即软链接硬链接

🌟首先是软链接,软链接又叫做符号链接,我们通过 ln -s 命令进行创建。

ls -s 原文件路径 软链接名

 

🌟可以看到出现了一个新的文件叫做 text.soft 并指向了我们原来的文件,尝试运行后,我们发现两个文件运行的结果都是一样的。

🌟实际上,软链接就像是我们 Windows 的快捷方式,内部存的就是原文件的地址,因此软链接文件都很小。

🌟即便我们在桌面这个目录下,依然能快速访问到其他文件夹中的应用。

 

🌟那软链接能否算作一个文件呢?之前我们便讲过用 ls -i 查询文件的 inode 编号,我们不妨观察一下它的 inode 编号。

🌟可以看到,两个文件拥有不同的 inode 编号,依照我们以前说过的,一个文件对应一个 inode,因此软链接与原文件为两个文件。

🌟若我们将原文件删除会发生什么呢?

🌟可以看到,我们运行软链接时,便会报错说找不到特定的文件。不仅删除文件,只是移动原文件的路径也会使软链接找不到原文件。

🌟为了我们创建的软链接文件能够随时挪动,在创建时使用绝对地址,即便移动软链接文件的地址也不会出错。

硬链接

🌟现在我们来讲硬链接,直接使用 ln 即可创建硬链接文件。

ln 原文件路径 硬链接名

🌟同样的,我们再看看它的 inode 编号。

🌟我们看到,硬链接文件与原文件使用的是同一个 inode,这表明着二者有同样的属性与内容。

🌟本质上,硬链接就是在目录中建立新文件名与旧 inode 的映射关系而已。

🌟这时候,我们将原文件删除,可以看到软链接文件已经失效,而硬链接文件仍能运行,便可验证我们上述观点。

🌟不知道在这个过程中,你是否发现了哪里不对劲?在上面这个位置的数跟原文件一样都是2,随着原文件删除就变成 1 了。

🌟这个数叫做硬链接数,表示当前文件一共有几个硬链接指向同一个 inode,表现于 inode 内部有一个 ref_count 变量,创建一个新文件时为 1,每当建立硬链接时加 1。而删除硬链接时也是减去 ref_count 的值,并清除目录里的映射关系,只有当 ref_count 为 0 时才会在位图中释放 inode 节点。

🌟可以这样理解,硬链接的本质就是在当前目录下创建一个名字不同的原文件。

🌟当我们不需要链接文件时可以使用 unlink 取消链接

unlink 链接文件名

🌟在当前路径下,我们创建了一个目录,这时我们发现刚创建的目录的硬链接数就有 2 了,这是为什么呢? 

🌟其实之前我们就学过了,ls -l 并非目录中的所有文件,需要用 -a 选项才能查看其中的隐藏文件。

🌟在使用 cd 命令时我们就经常使用 cd .. 返回上级目录。不出意料的话相信你已经猜到, . 和 .. 分别是当前目录和上级目录的硬链接

🌟但是用户是不能给目录建立硬链接的,若将这个权限交予用户则可能导致环路路径问题,会影响 OS 查找路径切换等功能。 

文件时间

🌟我们可以通过 stat 命令来查看文件的三个时间和其他信息。

stat 文件名

🌟而文件的三个时间分别是: 

  • Access: 上一次访问的时间
  • Modify: 上一次更改内容的时间
  • Change: 上一次更改属性的时间

🌟例如我们修改了文件权限后,文件的 Change 时间立刻就发生了改变。

 

🌟若是修改文件内容则会联动文件属性一起更改,因而 Modify 和 Change 时间都会改变。

🌟但 Access 时间由于访问次数过多不一定即时修改,需要注意。

库的介绍

🌟在以前,我们常常使用C语言提供给我们的头文件,例如 <stdio.h> 。但头文件中只提供方法说明,实际方法的实现位于库之中。头文件和库是有对应关系的,要组合在一起使用。

🌟平时,我们在使用编译器时都会有语法提醒功能,但这个功能有一个前提,需要先包含头文件。究其本质:编译器会自动将用户输入的内容在被包含的头文件中不断进行搜索,从而在前端界面中提示用户。

🌟而语法报错的检测也是一样,编译器在后台会不断编译将出错的信息反应给用户,进而完成语法检查。

🌟在某种情景下,你并不想把源码给别人,但是想让他用你的接口该怎么办?下面我们就一起学学如何制作一个自己的库。

静态库

🌟给别人库的本质就是给别人 .o 文件,在以前我们就曾将函数的声明与定义分离,声明位于头文件,在另一个 .c 文件中实现函数。

🌟我们实现了两个函数,并将其编译至 .o 文件。

🌟之后我们将源文件和两个 .o 文件一起编译 (*.o代指所有.o文件)。

g++ -o main main.cc *.o

🌟在源文件中,我们包含了两个头文件,在主函数内部分别调用两个函数。

#include<iostream>
#include"myadd.h"
#include"mysub.h"using namespace std;int main()
{cout<<myadd(20,50)<<endl;cout<<mysub(50,20)<<endl;return 0;
}

🌟运行main,我们便发现程序成功跑起来了。

 

🌟但是,现在两个 .o 文件还好,若是我们有很多的 .o 文件怎么办呢?能不能像压缩包一样给他打包起来呢?

打包

🌟我们通过这个命令,可以将几个.o文件打包成静态库。

🌟而实际的库名 = 文件名去掉前缀和后缀。

ar -rc 库文件名 *.o

 

🌟例如这里我们将上面两个 .o 文件打包成了 libmymath.a 这个文件,其中 libmymath.a 为文件名,而库名则是 mymath。

使用

🌟打包成库后,我们便可以将头文件和静态库交给别人了,此时别人手中只有库和 .h 文件,接下来该如何使用呢?

🌟由于这个库是我们自己写的,为第三方库,因此编译器并不认识这个库,编译的时候就需要如此编译。

g++ -o 可执行文件名 源文件名  -I 头文件路径 -L 库文件的路径  -l 第三方库名

 

 

🌟这下就可以运行 mian 这个可执行程序了。

🌟或是将库和头文件拷贝到系统默认的搜索路径下便可以省略中间两个选项。

默认头文件的搜索路径 /usr/include
默认库的搜索路径    /lib64

动态库

🌟接下来我们学习动态库的打包与使用,相较于动态库而言静态非常占用资源,会使磁盘中可以执行程序体积变大。

打包

🌟那我们如何打包成动态库呢?首先在生成 .o 文件时要使用 -fPIC 选项生成与位置无关码

g++ -c -fPIC *.cc    //一键编译.cc文件的.o文件

🌟再将.o文件打包,这次直接使用gcc/g++即可完成打包。

g++ -shared -o libmymath.so *.o

 

使用

🌟接下来我们像使用静态库那样,编译并运行可执行文件,虽然成功编译但在运行时却说找不到库的位置

🌟这是因为,静态库是将用户使用的二进制代码直接拷贝到可执行程序中,而动态库不是。

 

🌟在运行时 OS 会到三个地方查询文件中包含的动态库:

  • 环境变量 LD_LIBRARY_PATH
  • 系统路径 /lib64
  • 配置文件 /etc/ld.so.conf.d

🌟我们便可以从这三个角度入手,将库的路径填充到这几个路径下。

环境变量

🌟我们将库的路径插入到LD_LIBRARY_PATH这个环境变量中,再运行可执行文件,便发现成功运行了。

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:库的路径

 

系统路径

🌟由此,我们便在系统路径下建立了库文件的软链接。接下来便可正常运行了。

sudo ln -s 库的绝对链接  /lib64/软链接名称

 

 

配置文件

🌟第三种方式则是更改配置文件中的信息,我们将路径写入一个文件中,之后再将文件移动到对应路径下即可。

echo /home/Alpaca/linux-code-library/text.6.13/otherperson/mylib/ > Alpaca.conf //创建一个文件写入库的路径
sudo mv Alpaca.conf /etc/ld.so.conf.d  //移动文件到系统路径下
sudo ldconfig    //使配置文件生效

 

🌟再通过 ldd 查看便能够看到成功与库关联起来了,便可以成功运行。

🌟其中,只有第一种方式是一次性的,第二第三种方法都会持续生效。

原理

🌟动静态库又是如何加载呢?我们又该如何理解,就是接下来要讲的内容了。

🌟首先,可执行文件编译后,若使用了库中的函数,则会使用该函数在库中的位置进行替换。

🌟即,将可执行程序的外部符号替换成库中的具体地址

🌟如上图,此时可执行文件运行称为进程,内部 myadd 这个符号就被替换成了该函数在库中的位置即 mymath:123(假设)。

🌟当运行到 myadd 函数时,于是进程从虚拟内存找向物理内存,便发现这个库还没有打开,于是就去寻找这个叫做 mymath 的这个库,找到了就在内存中打开,而找不到的话就需要我们通过上面讲的三种方式帮助 OS 找到库。

🌟动态库的内容被打开后是会被映射到堆与栈之间的共享区的,此时的函数就在进程空间内完成映射,依旧在自己的地址空间内进行函数跳转。

🌟之后即使再运行一个进程,也是一样将物理内存的库映射到共享区,再进行函数跳转。

🌟而静态库被吐槽最多的还是因为它会直接将代码全部拷贝到可执行文件中,因此造成可执行文件过大的情况,那么动态库是如何处理的呢?

为了节省资源,动态库并不一定完全加载整个库。而是采用相对编址的方式进行。

🌟在那之前,使用动态库便必定会遇到一个问题,不停的进程运行程度不同,需要使用的库也是不同的,注定每一个进程共享区的空闲位置都是不确定的。

因此,一个库只有当他真正被映射进地址空间时,它的起始地址才能真正确定。

🌟我们在打包动态库前便使用 -fPIC 选项编译源文件,而这个选项的效果便是生成与位置无关码,所以,动态库中的地址都是偏移量!!!

🌟使用偏移量后,无论库被加载到共享区的哪个位置,只要用起始地址 + 偏移量便可以得到函数的地址


🌟好了,今天 软硬链接与动静态库 的相关内容到这里就结束了,如果这篇文章对你有用的话还请留下你的三连加关注。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue(脚手架创建)代理解决跨域问题

目录 为什么会出现跨域问题 什么是跨域 Vue CLI Vue2解决跨域问题 不重写路径 重写路径 vue.config.js代码 Vue3解决跨域问题 ViteVue解决跨域问题 vite.config.ts代码 总结 为什么会出现跨域问题 出于浏览器的同源策略的限制。同源策略是一种约定&#xff0c;它是…

Linux网络环境配置

第一种方式&#xff08;自动获取&#xff09;&#xff1a; 说明&#xff1a;登陆后&#xff0c;通过界面的来设置自动获取IP 特点&#xff1a;Linux启动后会自动获取IP 缺点&#xff1a;是每次自动获取的IP地址可能不一样 第二种方法&#xff08;指定IP)&#xff1a; 1、说明…

科技资讯|2023Q1中国电动汽车销量增长 29%,充电桩市场持续增长

根据市场调查机构公布的 2023 年第 1 季度中国国内电动汽车市场报告&#xff0c;比亚迪继续引领竞争日益激烈的电动汽车市场。 报告称 2023 年第 1 季度中国乘用电动汽车销量同比增长 29%&#xff0c;其中纯电动汽车&#xff08;BEV&#xff09;占销售额的近 70%、插电式混合…

Java——《面试题——网络篇》

前文 java——《面试题——基础篇》 Java——《面试题——JVM篇》 Java——《面试题——多线程&并发篇》 Java——《面试题——Spring篇》 Java——《面试题——SpringBoot篇》 Java——《面试题——MySQL篇》​​​​​​ Java——《面试题——SpringCloud》 Java…

Python笔记-1

Python安装问题 1.python是一门解释性的计算机程序语言。 2.IDLE就是我们写Python程序的地方&#xff08;小型的集成开发环境&#xff0c;编辑器&#xff09;。 3.Pycharm是一个大型的集成开发环境&#xff08;IDLE的扩展&#xff0c;不仅可以写&#xff0c;还能管理、调试&am…

基于PyQt5的桌面图像调试仿真平台开发(1)环境搭建

系列文章目录 基于PyQt5的桌面图像调试仿真平台开发(1)环境搭建 基于PyQt5的桌面图像调试仿真平台开发(2)UI设计和控件绑定 基于PyQt5的桌面图像调试仿真平台开发(3)黑电平处理 基于PyQt5的桌面图像调试仿真平台开发(4)白平衡处理 基于PyQt5的桌面图像调试仿真平台开发(5)…

HCIP(HCIA回顾)

OSI/RM 七层 应用层 表示层 会话层 传输层 区分不同的流量&#xff0c;定义传输方式。 端口号由16位二进制构成&#xff0c;范围为0~65535(其中0不作为传输层的端口使用)&#xff0c;所以真实取值范围为1~65535&#xff1b;其中&#xff0c;1~1023称为知名端口号。 1、可靠…

【Docker】Docker的优势、与虚拟机技术的区别、三个重要概念和架构及工作原理的详细讲解

前言 Docker 是一个开源的应用容器引擎&#xff0c;让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux或Windows操作系统的机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有任何接口。 &#x1f4d5;作者简介&#xff1a;热…

Ubuntu查看显卡信息

查看显卡信息&#xff0c;终端输入 lspci | grep VGA 输出结果 0000:65:00.0 VGA compatible controller: NVIDIA Corporation Device 24b0 (rev a1) 发现是十六进制码&#xff0c;进入网址PCI Devices查询&#xff0c;输入 24b0 并点击 Jump&#xff0c;得到结果 显卡型号…

软考A计划-系统集成项目管理工程师-项目整体管理-中

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列 &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff…

Redis实战案例8-缓存击穿及其解决方案和案例说明

1. 缓存击穿 缓存击穿是指一个被频繁访问&#xff08;高并发访问并且缓存重建业务较复杂&#xff09;的缓存键因为过期失效&#xff0c;同时又有大量并发请求访问此键&#xff0c;导致请求直接落到数据库或后端服务上&#xff0c;增加了系统的负载并可能导致系统崩溃 常见的解决…

数据库性能测试

目录 前言&#xff1a; 1.引入数据库驱动包 2.添加数据库配置元件 3、JDBCRequest参数化 4、Variablesnames参数使用方法&#xff1a; 前言&#xff1a; 数据库性能测试是测试数据库系统在各种条件下的性能和稳定性的过程。它可以帮助测试人员识别数据库系统的性能瓶颈&a…