穿越文件之海:Linux链接与库的奇幻旅程,软硬连接与动静态库

article/2025/6/7 12:15:57

文章目录

  • 引言
  • 1、软硬链接
    • 1.1、基本认知
    • 1.2、实现原理
    • 1.3、应用场景
    • 1.4、取消链接
    • 1.5、ACM时间
  • 2、动静态库
    • 2.1、认识库
    • 2.2、库的作用
  • 3、制作静态库
    • 3.2、静态库的使用
  • 4、制作动态库
    • 4.1、动态库的打包
    • 4.3、动态库的链接原理
  • 5、动态库知识补充

在这里插入图片描述

引言

在计算机的无形世界中,数据如河流般流淌,而文件系统则是河道的设计者。Linux,这位古老而睿智的守护者,为我们铺设了一条通往数字王国深处的道路。

今天,让我们踏上一段奇妙的旅程,探索Linux基础I/O中那些看似平凡却蕴含无限智慧的链接与库。

1、软硬链接

1.1、基本认知

对文件进行软硬链接非常简单,只需要通过ln -sln对文件进行链接即可,生成的链接文件类型为 l(普通文件为 -,目录文件为 d

对文件test.cc进行软链接,生成软链接文件 my-sort,其中软链接文件名可以自定义

ln -s test.cc my-soft.cc

需要注意在后续对软链接编译生成可执行程序时,如果my-soft.cc没有文件后缀,会存在无法识别问题
在这里插入图片描述
在这里插入图片描述

生成硬链接文件就更简单了,对文件 test.cc 进行硬连接,生成硬连接文件 my-hard,其中硬链接文件名也可以自定义

ln tets.cc my-hard

在这里插入图片描述
生成的软硬链接文件如何使用呢?

  • 像源文件一样使用即可,结果一模一样(因为当前软硬链接的都是同一个源文件)
    在这里插入图片描述
    虽然此时的软硬链接执行结果一致,但这两种链接方式在本质上有很大区别
  • 软链接文件的inode编号与源文件不同(独立存在),软连接文件比源文件小得多,并且 软连接文件->源文件
  • 硬链接文件与源文件共用一个 inode 编号(对源文件其别名),硬链接文件与源文件一样大,并且硬链接文件与源文件的链接数变成了 2
    在这里插入图片描述
    软链接文件依赖于源文件,而硬链接文件是源文件的别名

当我们将源文件删除后,软连接失效;硬链接仍然有效,不过硬链接数变为了 1

在这里插入图片描述

1.2、实现原理

软链接又称为符号链接,它是一个单独存在的文件,拥有属于自己的inode属性及相应的文件内容,不过在软连接的Data block中存放的是源文件的地址,因此软连接很小,并且非常依赖于源文件

在这里插入图片描述

  • 因此如果源文件被删除了,那么在执行软连接文件时,其中的地址就是一个无效地址(目标文件已丢失),此时就会报错 No such file or directory

  • 假设只是单纯的删除软连接文件,那么对源文件的内容没有丝毫影响,就好比 Windows 桌面上的快捷方式,有的人以为将快捷方式(软链接)文件删除了,就是在 “卸载” 软件,其实不是,如果想卸载软件,直接将其源文件相关文件夹全部删除即可

  • 硬链接并非创建一个相同的文件进行链接,而是在源文件所目录下的 【inode编号 与文件名对应表中】,新增 【inode 编号与硬链接文件名】的映射关系,并将 inode 结构体中的引用计数 +1,表示当前已成功硬链接上了一个文件

在这里插入图片描述

  • 当删除当前inode对应文件时,会 先判断 ref_count 是否为 1,如果是,才会将文件内容及其属性真正删除,-
  • 否则删除的只是 文件名 与 inode 编号的映射关系

这也就解释了为什么删除源文件后,硬链接文件不受任何影响,仅仅只是 硬链接数 - 1,同理,删除硬链接文件,也不会影响源文件

为什么新建目录的硬链接数为 2?

  • 因为一个目录在新建后,其中默认存在两个隐藏文件:. ..
  • 其中.表示当前目录,.. 表示上级目录
    Linux 中的目录结构为多叉树,即当前节点(目录)需要与父节点(上级目录)、子节点(下级目录)建立链接关系,并且还得知道当目录的地址,否则就会导致切换目录时出现错误

在这里插入图片描述
为了避免因用户的误操作而导致的目录环状问题,规定用户不能手动给目录建立硬链接关系,只能由 OS 自动建立硬链接

比如创建新目录后,默认与上级目录和当前目录建立硬链接文件,在当目录下创建新目录后,当前目录的硬链接数 + 1

在这里插入图片描述
小技巧:将目录的硬链接数 - 2,就可以知道该目录中有多少个目录了

ls -d	//只查看目录文件

1.3、应用场景

软链接可以当作快捷方式使用,比如快速运行一个藏的很深的可执行程序

硬链接

  • 可以用来当作目录移动的工具
  • 可以用来给重要的源文件起别名并使用,一旦发生删除等不可逆行为时,可以确保源文件的安全

注意: 硬链接并不是将源文件直接进行备份,而是新建立 inode 编号与硬链接文件名的映射关系,同时 struct inode 中的引用计数 ref_count++,只有当 ref_count == 1 时才会真正删除文件内容及属性,否则都只是在取消映射关系ref_count--

1.4、取消链接

取消链接的方式有两种:

  • 直接删除链接文件
  • 通过 unlink 取消链接关系

在这里插入图片描述

1.5、ACM时间

每一个文件都有三个时间:

  • 访问 Access
  • 修改属性 Change
  • 修改内容 Modify

简称为 ACM 时间

可以通过stat查看指定文件的 ACM 时间信息

在这里插入图片描述

2、动静态库

接下来学习动静态库的相关内容,了解程序运行时是如何调用资源的

2.1、认识库

常见的库文件:stdio.hstdlib.hstring.h

库分为 动态库 静态库

  • Linux 中,.a 后缀为静态库,.so 后缀为动态库
  • Windows 中,.lib 后缀为静态库,.dll 后缀为动态库

虽然不同环境下的后缀有所不同,但其工作原理是一致的

库命名

  • 比如 libstdc++.so.6
  • 去掉前缀跟后缀,最终库名为 stdc++

查看当前环境中的库文件

ls /usr/lib/x86 64-linux-gnu/libc*

在这里插入图片描述

  • 无论是 C语言 还是 C++,在编写程序时,一定离不开库文件,比如之前模拟实现的 FILE 类型,就位于 stdio.h 这个库中。
  • 动态库优势比静态库明显,因此在编译代码时,默认采用动态链接的方式,如果想指定为静态链接编译,只需要在 gcc/g++ 语句后面加上 -static 即可(前提是得有静态库)
    在这里插入图片描述
    观察可得,静态库编译得到的程序大小远大于动态库。

关于动静态库的优缺点可以看看下面这个表格:
在这里插入图片描述
注意静态库是将所需要的函数代码拷贝到源文件中直接使用,而动态库是通过动态链接的方式,进行函数链接使用

2.2、库的作用

所以,库文件到底有什么用?

  • 提高开发效率

系统已经预装了 C/C++ 的头文件和库文件,头文件提供说明,库文件提供方法的实现

  • 头和库是有对应关系的,需要组合使用
  • 头文件在预处理阶段就已经引入了,链接的本质就是在链接库

简言之,如果没有库文件,那么你在开发时,需要自己手动将 printf 等高频函数编写出来,因此库文件可以提高我们的开发效率,比如 Python 中就有很多现成的库函数可以使用,效率很高

语法提示是如何做到的?

  1. 安装开发环境
  • 实际上是在安装编译器、开发语言配套的库和头文件
  1. 编译器的 语法提示功能来源于头文件(语法提示其实就是搜索)

  2. 我们在写代码时,开发环境是怎么知道语法错误或其他错误的?

  • 编译器有命令行模式,还有其他自动化模式,编写代码时,不断进行主动编译,排查错误

3、制作静态库

现在有一些简单的计算 demo 函数,能满足整型的 ± 计算,将这些代码作为库进行打包
myadd.h

#pragma once//声明功能
int add(int x, int y);

myadd.c

#include "myadd.h"//定义功能
int add(int x, int y)
{return x + y;
}

mysub.h

#pragma once//声明功能
int sub(int x, int y);

mysub.c

#include "mysub.h"//定义功能
int sub(int x, int y)
{return x - y;
}

主函数中将对这些自定义的库函数进行调用
test.c

#include <stdio.h>
#include "myadd.h"
#include "mysub.h"int main()
{printf("2 + 3 = %d\n", add(2, 3));printf("18 - 6 = %d\n", sub(18, 6));return 0;
}

3.1、静态库的打包
静态库的打包主要分为以下两步:

  • 将源文件进行 预处理->编译->汇编,生成可链接的二进制 .o 文件
  • 通过指令将 .o 文件打包为静态库

将文件编译为 .o 二进制文件

gcc -c myadd.c mysub.c

在这里插入图片描述
将所有的.o文件打包为一个静态库(库名自定义)
其中的mycalc为库名

ar -rc libmycalc.a *.o

在这里插入图片描述

  • ar GNU 提供的归档工具,常用来将目标文件打包为静态库
  • 我们还可以使用 ar 反向查看静态库中的具体文件
    ar -tv 静态库文件

在这里插入图片描述
获得静态库后,就可以进行使用了

注:此时的 .h、.c、.o 文件位于 lesson20 文件夹中,而静态库文件 .a 位于mylib文件夹中

3.2、静态库的使用

方法一:通过指定路径使用静态库

如果直接编译程序,会出现编译失败的情况,因为编译器不认识第三方库(需要提供第三方库的路径及库名)

在这里插入图片描述

第一方库:语言提供
第二方库:操作系统提供
第三方库:other 提供的库,比如当前我们直接打包的静态库

对于自己写的的第三库的使用,需要标注三个参数:

-I 所需头文件的路径 需要将所需头文件的路径加上,因为此时路径已经为所需路径,因此此处为 ./
-L 所需库文件的路径 这里加的是库文件的路径,也为 ./
-l 待链接静态库名 所需要链接的静态库名字,这里为 mycalc

 gcc -o ret test.c  -I./ -L./ -lmycalc

在这里插入图片描述

为什么编译 C/C++ 代码时,不需要指定路径?

  • 因为这些库都是系统级的,gcc/g++ 默认找的就是 stdc/stdc++ 库

方法二:将头文件和静态库文件安装至系统目录中

除了这种比较麻烦的指定路径编译外,我们还可以将头文件与静态库文件直接安装在系统目录中,直接使用,无需指定路径(需要指定静态库名)

所谓的安装软件,就是将自己的文件安装到系统目录下

sudo cp ./*.h /usr/include/
sudo cp libmycalc.a /usr/lib/x86_64-linux-gnu

在这里插入图片描述

注意: 将自己写的文件安装到系统目录下是一件危险的事(导致系统环境被污染),用完后记得手动删除

在这里插入图片描述

4、制作动态库

除了可以制作静态库外,我们还可以制作动态库,这里用的例子和上面一样

4.1、动态库的打包

动态库不同于静态库,动态库中的函数代码不需要加载到源文件中,而是通过 与位置无关码 ,对指定函数进行链接使用

动态库的打包也同样分为两步:

  • 编译源文件,生成二进制可链接文件,此时需要加上 -fPIC 与位置无关码
  • 通过 gcc/g++ 直接目标程序(此时不需要使用 ar 归档工具)

将源文件编译为.o二进制文件,此时需要带上 fPIC 与位置无关码

gcc -c -fPIC *.c

在这里插入图片描述
将所有的 .o 文件打包为动态库(借助 gcc/g++)

gcc -o libmycalc.so *.o -shared

在这里插入图片描述
获得动态库后,就可以进行使用了

注:此时的 .h、.c、.o 文件位于 myinclude 文件夹中,而动态库文件 .so 位于 libmycalc.so 文件夹中

4.2、动态库的链接与使用
像使用静态库一样使用动态库(指定路径及库名),编译成功,但运行失败!

在这里插入图片描述
为什么会出现这种问题?因为当前只告诉了编译器动态库的位置,没有告诉 OS

通过ldd查看程序链接情况:
在这里插入图片描述
当前尚未链接

运行时,OS ·是如何链接·动态库

  1. 环境变量·LD_LIBRARY_PATH (默认没有这个环境变量),将第三方动态库路径添加至此环境变量中(临时方案)
  2. sudo 在 /lib64/ 目录下建立动态库的软链接
  3. 更改配置文件/etc/ld.so.conf.d这个目录中都是各种动态库配置文件,创建文件 xx.conf 至目录中(文件中存储的是第三方动态库的路径)ldconfig 令配置文件生效

以上三种方式都可以正常使用动态库,下面就来逐个进行尝试

方法一:通过环境变量解决

添加动态库路径至 LD_LIBRARY_PATH 环境变量中

在这里插入图片描述
环境变量 LD_LIBRARY_PATH 是程序在进行动态库查找时的默认搜索路径

注意: 更改环境变量只是临时方案,重新登录后会失效

方法二:将动态库的软链接文件存入系统目录中

 sudo ln -s /home/ccc/lesson20/myinclude/libmycalc.so /lib64/

在这里插入图片描述
注意: 创建软连接文件时,需要使用绝对路径

方法三:更改配置文件中的信息

echo /home/ccc/lesson20/myinclude/libmycalc.so > ccc.conf
sudo mv ccc.conf /etc/ld.so.conf.d/
ls /etc/ld.so.conf.d/ccc.conf

在这里插入图片描述
注意: 后两种方法都可以做到永久生效(因为存入了系统目录中),但在使用完后最好删除,避免污染系统环境

4.3、动态库的链接原理

程序在链接动态库函数时,是通过 动态库起始地址 + 所链接函数偏移量 的方式进行链接访问的,而这个偏移量就是 fPIC位置无关码

地址其实就两种:绝对地址相对地址.

  • 静态链接时,将可链接的二进制文件加载至程序中,直接通过 绝对地址 进行链接,假设函数被调用了多次,就会导致代码冗余等问题;
  • 动态链接采用 相对地址 的方式进行链接,同一个函数的 动态库起始地址 + 所链接函数偏移量 值相同,代码只需要加载一份,并且可以任意位置进行函数调用(与位置无关)

在这里插入图片描述
动态库中所有地址都是偏移量,默认从 0 开始

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

  • 链接库中的函数时,通过 动态库的起始地址 + 函数偏移量 的方式链接函数
  • 这种方法不论在什么位置,都可以随便链接函数(与位置无关)
  • 与位置无关码:动态库中地址,是偏移量

5、动态库知识补充

  • 当同时拥有 静态库动态库 时,默认采用动态链接

  • 如果只有静态库,但又不指定静态链接,会发生静态库文件

  • 静态链接生成的程序比动态链接大得多,并且内含静态库的动态链接程序,也比纯粹的动态链接程序大,说明程序不是·非静即动,可以同时使用动态库与静态库

本篇关于软硬链接与动静态库的介绍就暂告段落啦,希望能对大家的学习产生帮助,欢迎各位佬前来支持斧正!!!

在这里插入图片描述


http://www.hkcw.cn/article/VHmkSFilks.shtml

相关文章

2024年认证杯SPSSPRO杯数学建模D题(第二阶段)AI绘画带来的挑战解题全过程文档及程序

2024年认证杯SPSSPRO杯数学建模 D题 AI绘画带来的挑战 原题再现&#xff1a; 2023 年开年&#xff0c;ChatGPT 作为一款聊天型AI工具&#xff0c;成为了超越疫情的热门词条&#xff1b;而在AI的另一个分支——绘图领域&#xff0c;一款名为Midjourney&#xff08;MJ&#xff…

6.3 计算机网络面试题

网络OSI模型和TCP/IP模型分别介绍一下 OSI 应用层&#xff1a;http htps DNS 为应用程序提供统一的接口表示层&#xff1a;将数据转换为兼容另一个系统能识别的格式会话层&#xff1a; 建立管理终止表示层实体之间的通信会话传输层: tcp udp 负责端到端的数据传输网络层: ip …

LeetCode 高频 SQL 50 题(基础版) 之 【高级查询和连接】· 下

上部分链接&#xff1a;LeetCode 高频 SQL 50 题&#xff08;基础版&#xff09; 之 【高级查询和连接】 上 题目&#xff1a;1164. 指定日期的产品价格 题解&#xff1a; select product_id,10 price from Products group by product_id having min(change_date) > 201…

ssm学习笔记day04

RequestMapping 首先添加依赖 Maven的配置 测试 在controller创建HelloController&#xff0c;如果只加RequestMapping&#xff0c;默认跳转到新页面 如果要是加上ResponseBody就把数据封装在包(JSON)&#xff0c;标签RestController是前后分离的注解&#xff08;因为默认用…

Spine工具入门教程4之网格与权重

1、概念 网格的定义&#xff1a; 启用网格&#xff0c;可以在图片内设置多边形&#xff0c;操纵多边形的顶点可以让图片变形。 权重的定义&#xff1a; 图解网格和权重的操作方法&#xff1a; 2、调整网格/权重 &#xff08;1&#xff09;设置网格 目前调整小臂骨骼对图片不…

吉他入门个人学习笔记

目录 一.一二期 1. 十二平均律 2.调音 3.弦数-音名-唱名 三.第三期 1.43231323训练 2.c大调二十四品常用音阶图 四.第四期 1.小星星 2.爬格子训练 五.第五期 六.第六期——大三和弦 和弦总览 1.C和弦 2.D和弦 3.E和弦 4.G和弦 5.A和弦 第七期.小三和弦 五百…

队列的讲解:C++队列的使用

一.队列的介绍&#xff1a; 队列是C/C中最基础的数据结构之一&#xff0c;队列本质上是一种线性表。它遵循着先进先出(fifo)的特点&#xff0c;在队列中一般在队尾插入&#xff0c;队头出队。这就相当于排队一样&#xff0c;刚入队的人需要排在队尾(rear)&#xff0c;每次出队…

使用Process Explorer、System Informer(Process Hacker)和Windbg工具排查软件高CPU占用问题

目录 1、问题现象 2、使用Process Explorer和System Informer&#xff08;该工具原先叫Process Hacker&#xff09;查看占用CPU高的线程 3、使用System Informer工具时发现了一个关键细节 4、将Windbg附加到软件进程上&#xff0c;根据System Informer中显示的线程id到Wind…

华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建

华为云FlexusDeepSeek征文&#xff5c;DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色&#xff0c;华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型&#xff0c;能助力我们轻松驾驭 DeepSeek-V3/R1&#xff0c;本文中将分享如何…

传统业务对接AI-AI编程框架-Rasa的业务应用实战(1)--项目背景即学习初衷

我的初衷&#xff1a;我想学习AI。具体的方向是这样的&#xff1a;原本传统的平台业务去对接智能体。比如发票业务&#xff0c;发票的开具、审核、计税、回款等。根据用户在业务系统前台界面输入若干提示词 或者 语音输入简短语音信息&#xff0c;可以通过智能体给出需要处理的…

【八股消消乐】索引失效与优化方法总结

&#x1f60a;你好&#xff0c;我是小航&#xff0c;一个正在变秃、变强的文艺倾年。 &#x1f514;本专栏《八股消消乐》旨在记录个人所背的八股文&#xff0c;包括Java/Go开发、Vue开发、系统架构、大模型开发、具身智能、机器学习、深度学习、力扣算法等相关知识点&#xff…

Java面试八股--06-Linux篇

目录 一、Git 1、工作中git开发使用流程&#xff08;命令版本描述&#xff09; 2.Reset与Rebase&#xff0c;Pull与Fetch的区别 3、git merge和git rebase的区别 4、git如何解决代码冲突 5、项目开发时git分支情况 二、Linux 1、Linux常用的命令 2、如何查看测试项目的…

动态规划-647.回文子串-力扣(LeetCode)

一、题目解析 这里的子字符串是连续的&#xff0c;与之前的子序列不同&#xff0c;这里需要我们统计回文子串的数目。 二、算法原理 这里也有其他算法可以解决该问题&#xff0c;如中心扩展算法 时间复杂度O(N^2)/空间复杂度O(1)&#xff0c;马拉车算法(具有局限性) 时间复杂…

条形进度条

组件 <template><view class"pk-detail-con"><i class"lightning" :style"{ left: line % }"></i><i class"acimgs" :style"{ left: line % }"></i><view class"progress&quo…

大模型赋能:金融智能革命中的特征工程新纪元

一、AI进化论&#xff1a;从“判别”到“生成”的金融新战场 1.1 判别式AI的“痛点”与大模型的“破局” 想象这样一幅画面&#xff1a;银行风控模型像老式收音机&#xff0c;需要人工反复调试参数才能捕捉风险信号&#xff1b;而大模型则是智能调音台&#xff0c;能自动“听…

HA: Wordy靶场

HA: Wordy 来自 <HA: Wordy ~ VulnHub> 1&#xff0c;将两台虚拟机网络连接都改为NAT模式 2&#xff0c;攻击机上做namp局域网扫描发现靶机 nmap -sn 192.168.23.0/24 那么攻击机IP为192.168.23.128&#xff0c;靶场IP192.168.23.130 3&#xff0c;对靶机进行端口服务探…

技巧小结:外部总线访问FPGA寄存器

概述 需求&#xff1a;stm32的fsmc总线挂载fpga&#xff0c;stm32需要访问fpga内部寄存器 1、分散加载文件将变量存放到指定地址即FPGA寄存器地址 sct文件指定变量存储地址&#xff0c;从而可以直接访问外设&#xff0c;&#xff08;28335也可以&#xff0c;不过用的是cmd文件…

深入理解 x86 汇编中的重复前缀:REP、REPZ/REPE、REPNZ/REPNE(进阶详解版)

一、重复前缀&#xff1a;串操作的 “循环加速器” 如果你写过汇编代码&#xff0c;一定遇到过需要重复处理大量数据的场景&#xff1a; 复制 1000 字节的内存块比较两个长达 200 字符的字符串在缓冲区中搜索特定的特征值 手动用loop指令编写循环&#xff1f;代码冗长不说&a…

【PCB设计】STM32开发板——原理图设计(电源部分)

一、PCB设计流程 二、准备工作 1.点击文件新建工程并命名 2.新建图页 在绘制较为复杂的原理图时&#xff0c;可以建立多个图页&#xff0c;使得原理图更加清晰。 右击原理图→新建图页 右击→重命名 3.设计规则相关配置 取消勾选第22个 4.调整页面大小 5.放置“电源树”图片…

C++仿RabbitMQ实现消息队列

前言 本项目将使用 C 在 Linux&#xff08;CentOS 7.6&#xff09; 环境下开发一个仿 RabbitMQ 的简易消息队列。 开发和调试环境如下&#xff1a; 操作系统&#xff1a;Linux (CentOS 7.6) 编辑器&#xff1a;Visual Studio Code / Vim 编译器&#xff1a;g&#xff08;GNU…