Linux基础IO【软硬链接和动静态库】

简介: Linux软硬链接和动静态库,包括动软硬链接的原理和操作,动静态库的打包和使用等丰富内容,详细讲解,干货满满!

Linux基础IO【软硬链接和动静态库】

平时我们在运行软件时并不会到软件目录中去找到.exe文件来打开,而是直接从桌面双击快捷方式运行,这个快捷方式就是其对应的软链接文件,下面就来一起看看软硬链接和动静态库的相关知识吧


1. 软硬链接

1.1 现象理解

通过指令ln -s,就可以对文件进行软链接

ln -s 文件名 软链接文件名  //生成软链接文件


通过指令ln,即可对文件进行硬链接

ln 文件名 硬链接文件名  //生成硬链接文件

软硬链接的文件可以和源文件一样使用

软硬连接的区别

  • 软链接是一个独立的连接文件,有自己独立的inode号,就有自己的属性,同时软链接文件指向源文件(依赖于),大小比源文件小得多

  • 硬链接和目标文件共用同一个inode,大小与源文件一样大,硬链接的本质就是建立了新的文件和inode的映射关系,相当于给文件起别名

如果我们删除源文件,软链接将会失效,而硬链接依然有效

1.2 链接原理

软链接原理

  • 软链接文件是一个单独存在的文件,有自己的inode属性及相应的文件内容,在软连接的Data block中存放的是源文件的地址,因此软连接很小,并且依赖于源文件
  • 源文件被删除,软链接文件就会失效,因为此时里面存放的已经是一个无效的地址,相反如果删除软链接文件,对源文件的内容没有影响,就比如在Windows下删除桌面的快捷方式一样

硬链接原理

  • 硬链接实质上就是一个指针,指向文件索引节点,硬链接的本质就是把源文件的属性拷贝一份,然后把文件名改一下即起别名,该文件名对目标文件inode做映射,直接指向目标文件inode
  • 源文件有对应的inode结构体,其中有用来记录硬链接数量的变量,硬链接后,硬链接文件和源文件inode一样,并且此inode中的硬链接数变量进行+1操作,表示当前已成功硬链接上了一个文件
  • 当删除当前inode对应文件时,会先判断硬链接数变量是否为1,如果是,才会将文件内容及其属性真正删除,否则删除的只是文件名与inode编号的映射关系。所以删除源文件后,硬链接文件不受影响,只是硬链接数-1,同样的,删除硬链接文件,也不会影响源文件

新建目录的硬链接数默认为2,这是为什么呢?

  • 因为目录中默认存在两个隐藏文件,. 表示当前目录,.. 表示上级目录
  • 将目录的硬链接数-2,就可以得到当前目录下的目录数量了

不能手动给目录建立硬链接

  • Linux下的目录结构为多叉树结构,当目录需要与上级目录和下级目录建立链接关系,为了避免用户的链接操作导致目录成环问题,规定只能由OS自动给目录建立硬链接

1.3 应用场景

当一个文件层次太深时,可以直接软链接在当前目录对其进行相关操作

硬链接一般用来当目录移动个工具,再就是给文件起别名来使用,确保源文件不被破坏

1.4 取消链接

通过指令unlink,可以取消链接关系

当然也可以暴力点,直接删除链接文件来取消链接

2. 动静态库

2.1 初识库

Linux下库分为静态库和动态库

  • 静态库:后缀为.a,程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库
  • 动态库:后缀为.so,程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码

Windows下, 静态库后缀为.lib,动态库后缀为.dll

库的命名规则

  • 库命名:开头(lib) + 命名 + 后缀(.a / .so)
  • 例如,libstdc++.so.6,去掉前缀和后缀就得到了stdc++,也就是库名

Linux的库在/usr/lib64目录下

find /usr/lib64/libc*

头文件在/usr/include目录下

2.2 库的意义

这里的库到底是什么呢?头文件又是什么呢?

  • 打开一个库文件,我们发现它是个二进制文件,头文件我们打开发现都是函数的声明,而库中对应的就是对应函数功能实现,头文件在预处理阶段就已经引入了,链接的本质就是在链接库
  • 我们在安装编译器软件的时候,其实就是安装头文件和库文件。另外还有使用编译器的时候,编译器会有自动提醒头文件或者函数等功能,这些都是通过遍历头文件和库文件来匹配完成检索的,还有语法报错功能,这也是编译器来进行检索功能

所以库的作用到底是什么呢?

  • 头文件和对应的库文件一安装环境就有了,其中的功能接口可以直接拿来用,不用自己造轮子,这样就提高了开发效率

3. 静态库制作

先提供一个加减方法

我们可以将其打包成静态库

3.1 静态库打包

静态库的打包步骤:

第一步:将源文件进行预处理->编译->汇编,生成可链接的二进制.o文件

gcc -c 源文件

第二步:使用ar指令将.o文件打包成静态库

ar -rc lib库名.a *.o  //生成静态库

这样静态库就打包好了

通过指令ar -tv可以查看静态库中的具体文件

3.2 静态库使用

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

自己写的第三库,需要注明三个参数才能使用:

  1. -I + 所需头文件的路径
  2. -L + 所需库文件的路径
  3. -l + 待链接静态库名
gcc -o 目标文件名 源文件名 -I头文件路径 -L库文件路径 -l静态库名

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

  • 因为使用的库都是系统级的,gcc/g++ 默认就是stdc/stdc++系统库里面找

第二种方法:将头文件和静态库文件安装至系统目录中,这里需要指定静态库名

sudo cp ./头文件名/*.h /usr/include/
sudo cp ./静态库文件名/*.a /lib64/

注意:将自己写的文件安装到系统目录下,使用完后记得手动删除,避免造成系统环境污染

4. 动态库制作

4.1 动态库打包

动态库是通过与位置无关码,来达到对指定的函数进行链接使用

动态库的打包步骤:

第一步:还是编译源文件来生成二进制可链接文件,需要加上与位置无关码-fPIC

gcc -c -fPIC *.c

这时候生成的.o文件中就有了与位置无关码

第二步:使用gcc/g++直接将所有的.o文件打包成动态库,需要加上-shared选项

gcc/g++ -o lib动态库名.so *.o -shared  //打包动态库

这样动态库就打包好了

4.2 动态库使用

像使用静态库一样,通过指定路径和动态库库名来使用动态库

这里我们发现编译成功了但是运行程序却失败了,这是为什么呢?

  • 这是因为当前只告诉了gcc编译器动态库的位置,并没有告诉OS

我们通过指令ldd来查看一下程序的链接情况

可以发现当前的状态是未链接

这里有三个方法可以让OS链接动态库

方法一:环境变量法(临时方案)

将动态库路径添加到 LD_LIBRARY_PATH 环境变量中,环境变量,LD_LIBRARY_PATH是程序在进行动态库查找时的默认搜索路径

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:动态库路径

环境变量重新登录后会失效,所以这是个临时方法

方法二:软链接法

/lib64/ 目录下建立动态库的软链接,将动态库的软链接文件存入系统目录中

sudo ln -s 动态库文件绝对路径/动态库文件名 /lib64/动态库文件名

注意:创建软链接文件时,必须使用绝对路径!

方法三:配置文件法

各种动态库配置文件在/etc/ld.so.conf.d目录中,创建.conf文件,文件中存储第三方动态库的路径,然后将文件移动到此目录中,通过指令ldconfig 使配置文件生效(更新配置文件)

echo 动态库文件绝对路径 > .conf文件
sudo mv .conf文件 /etc/ld.so.conf.d/
sudo ldconfig

方法二和方法三都存入了系统目录中,是永久有效的

5. 动静态库加载和补充

5.1 动静态库加载

静态库加载

  • 当程序形成可执行程序的时候,这个可执行程序中已经包含了静态库中被转化为二进制代码的函数功能实现,此时形成.exe文件后,把静态库删除了依旧可以运行正常,这相比动态库,静态库的可执行文件要大的多,因为动态库中被转化为二进制程序的函数功能实现并不是直接拷贝到.exe文件中的。所以总结出一个结论:静态库非常占用资源(磁盘资源等)

动态库加载

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

解释一下这个图

  • 这里需要注意的是虚拟内存空间中有共享区这个区域,这个区域就是用来存放应的动态库文件中相关库函数名的地址,运行程序时,需要用到某个函数时就对应的把动态库文件也加载到内存中,通过函数名地址在共享区找相对应的函数地址,然后加载到物理内存中,再给CPU进行计算
  • 所以,把对应的动态库文件删除,就好像找不到相对应的函数,并且它是只有一份的,这样就可以随时拿来使用,没有静态库那么占用资源

上面动态库使用中我们用到了-fPIC这个选项,它与位置无关码是什么呢?

  • 也就是这里的函数名调用和函数地址对应关系和位置并不相关,可以这样理解:每次加载到内存中虚拟内存中的共享区这个区间的起始地址是不一样的,这里是根据对应的起始位置的偏移量找到函数实现的,所以和位置(绝对位置)无关

5.2 知识补充

生成静态链接文件,只需要加上-static选项即可

gcc -o 目标文件名 源文件名 -I头文件路径 -L库文件路径 -l静态库名 -static

链接补充

  • 编译器进行链接的时候,如果同时有动态库和静态库,优先动态链接,没有动态库只有静态库时,只能静态链接;如果都没有,则程序运行不了
  • 只有静态库,又不指定静态链接,会默认使用动态链接,生成的程序内含有静态库

Linux基础IO【软硬链接和动静态库】,到这里就介绍结束了,本篇文章对你由帮助的话,期待大佬们的三连,你们的支持是我最大的动力!

文章有写的不足或是错误的地方,欢迎评论或私信指出,我会在第一时间改正

相关实践学习
CentOS 7迁移Anolis OS 7
龙蜥操作系统Anolis OS的体验。Anolis OS 7生态上和依赖管理上保持跟CentOS 7.x兼容,一键式迁移脚本centos2anolis.py。本文为您介绍如何通过AOMS迁移工具实现CentOS 7.x到Anolis OS 7的迁移。
目录
相关文章
|
4月前
|
安全 Linux vr&ar
Linux的动态库和静态库
Linux的动态库和静态库
|
4月前
|
Linux 索引
在Linux中,符号链接与硬链接有何区别?
在Linux中,符号链接与硬链接有何区别?
|
2月前
|
Linux API 开发工具
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
ijkplayer是由B站研发的移动端播放器,基于FFmpeg 3.4,支持Android和iOS。其源码托管于GitHub,截至2024年9月15日,获得了3.24万星标和0.81万分支,尽管已停止更新6年。本文档介绍了如何在Linux环境下编译ijkplayer的so库,以便在较新的开发环境中使用。首先需安装编译工具并调整/tmp分区大小,接着下载并安装Android SDK和NDK,最后下载ijkplayer源码并编译。详细步骤包括环境准备、工具安装及库编译等。更多FFmpeg开发知识可参考相关书籍。
98 0
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
|
4月前
|
Linux API
在Linux中,程序产生了库日志虽然删除了,但磁盘空间未更新是什么原因?
在Linux中,程序产生了库日志虽然删除了,但磁盘空间未更新是什么原因?
|
5月前
|
Oracle 关系型数据库 Linux
讲解linux下的Qt如何编译oracle的驱动库libqsqloci.so
通过这一连串的步骤,可以专业且有效地在Linux下为Qt编译Oracle驱动库 `libqsqloci.so`,使得Qt应用能够通过OCI与Oracle数据库进行交互。这些步骤适用于具备一定Linux和Qt经验的开发者,并且能够为需要使用Qt开发数据库应用的专业人士提供指导。
160 1
讲解linux下的Qt如何编译oracle的驱动库libqsqloci.so
|
4月前
|
Linux 网络安全 API
【Azure 应用服务】App Service For Linux 环境中,如何从App Service中获取GitHub私有库(Private Repos)的Deploy Key(RSA key)呢?
【Azure 应用服务】App Service For Linux 环境中,如何从App Service中获取GitHub私有库(Private Repos)的Deploy Key(RSA key)呢?
|
4月前
|
小程序 Linux 开发者
Linux之缓冲区与C库IO函数简单模拟
通过上述编程实例,可以对Linux系统中缓冲区和C库IO函数如何提高文件读写效率有了一个基本的了解。开发者需要根据应用程序的具体需求来选择合适的IO策略。
34 0
|
5月前
|
网络协议 Ubuntu Linux
查询Linux中网络链接的状态:networkctl
【7月更文挑战第18天】
114 0
查询Linux中网络链接的状态:networkctl
|
4月前
|
存储 Java
【IO面试题 四】、介绍一下Java的序列化与反序列化
Java的序列化与反序列化允许对象通过实现Serializable接口转换成字节序列并存储或传输,之后可以通过ObjectInputStream和ObjectOutputStream的方法将这些字节序列恢复成对象。
|
5月前
|
Java 大数据
解析Java中的NIO与传统IO的区别与应用
解析Java中的NIO与传统IO的区别与应用