系列文章
收录于【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语言提供给我们的头文件,例如 。但头文件中只提供方法说明,实际方法的实现位于库之中。头文件和库是有对应关系的,要组合在一起使用。
🌟平时,我们在使用编译器时都会有语法提醒功能,但这个功能有一个前提,需要先包含头文件。究其本质:编译器会自动将用户输入的内容在被包含的头文件中不断进行搜索,从而在前端界面中提示用户。
🌟而语法报错的检测也是一样,编译器在后台会不断编译将出错的信息反应给用户,进而完成语法检查。
🌟在某种情景下,你并不想把源码给别人,但是想让他用你的接口该怎么办?下面我们就一起学学如何制作一个自己的库。
静态库
🌟给别人库的本质就是给别人 .o 文件,在以前我们就曾将函数的声明与定义分离,声明位于头文件,在另一个 .c 文件中实现函数。
🌟我们实现了两个函数,并将其编译至 .o 文件。
🌟之后我们将源文件和两个 .o 文件一起编译 (*.o代指所有.o文件)。
g++ -o main main.cc *.o
🌟在源文件中,我们包含了两个头文件,在主函数内部分别调用两个函数。
#include
#include"myadd.h"
#include"mysub.h"
using namespace std;
int main()
{
cout
🌟运行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.fwqtg.net
接口和抽象类是 Java 中两种实现抽象和多态的方法。它们之间有一些区别,但也有一些相似之处。这一节我们将通过详细的例子来更深入地了解接口和抽象类。 2.3.1. 接口 接口可以定义一组方法签名,但不能包含方法的实现。一个类可以实现多个接口,实现接口的类必须实…