Liunx下的文件管理系统与软硬链接以及动静态库的制作使用理解

文章目录

  • 前言
  • 1.磁盘的相关介绍
  • 2.磁盘寻址方式的抽象
  • 3.区组块的划分管理
      • 1.文件系统是怎么分区的,又是谁分区的,什么时候分区的?
      • 2.文件inode和文件名对比
      • 3.目录是文件吗?如果目录是文件,那么目录的文件内容是什么呢?
      • 4.再次理解文件在磁盘上的创建和删除
      • 5.直接索引和间接索引
      • 6.有没有一种可能一个组内的inode节点使用完了但是DataBlocks没有使用完,或者是DataBlocks使用完了但是inode没有使用完。
  • 4.软硬链接
    • 1.软链接
    • 2.硬链接
  • 5.动静态库
    • 1.静态库的制作与使用
    • 2.动态库的制作与使用
    • 3.动静态库的加载过程

前言

本文主要是对Liunx下的磁盘文件进行介绍,现在主流的Liunx磁盘文件系统是Ext3.0或者Ext4.0。这里主要是以相对简单的Ext2.0进行介绍的,通过认识Liunx下的磁盘文件系统,让我们对操作系统管理磁盘的理解更加深刻。同时还会对软硬链接和动静态库进行详细介绍。


1.磁盘的相关介绍

在介绍文件系统之前,我们先来了解一下磁盘这种硬件设备。我们知道当我们没有打开文件的时候,文件都是存储是在磁盘(硬盘上)的,那么磁盘结构是怎么样的呢?我们看看下图:

Liunx下的文件管理系统与软硬链接以及动静态库的制作使用理解_第1张图片

通过上述的介绍我们大概知道了磁盘的结构。那我们现在思考一个问题,如何在一个磁盘上确定一个扇区呢?答案很简单,我们先确定一个盘面,盘面根据磁头( magnetic head)就可以确定了。再根据根据磁道也就是柱面(cylinder)定位扇区,最后在某个磁道上找到该扇区(cylinder),就可以定位到某个扇区了。这种磁盘寻址的方式是chs.

2.磁盘寻址方式的抽象

通过上述我们知道磁盘寻址定位方式chs,但是这是物理寻址方式,操作系统是软件层,这套寻址方式看到不适操作系统。操作系统的上面怎么寻址管理磁盘上的数据的呢?我们先看看下面这张图片。

Liunx下的文件管理系统与软硬链接以及动静态库的制作使用理解_第2张图片

我们把磁盘抽象成这种类似于数组的线性结构,操作系统对磁盘的管理转化成了对数组的管理。我们可以将每个扇区给一个编号,像通过数组下标确定数组元素位置那样,我们通过这个编号也可以确定每个扇区的位置,这样就有了一套属于操作系统的寻址方式。这种地址被称为LBA逻辑地址。这样的话就操作系统就可以实现对磁盘的管理了。这套逻辑地址其实和之前博客介绍的虚拟地址是类似的,都是操作系统为了便于管理逻辑抽象出的地址。

3.区组块的划分管理

我们知道对比cpu来说外访问效率很低,这样的话就要求系统每次进行IO请求尽可能多的进行数据交换。所以内存处理外IO请求的时候一般都是以4kb大小的数据块进行数据交换处理的。也就是说系统可以将8个512字节大小的扇区组成一个块结构为基本单位进行IO交互。那么也就说文件系统以4kb大小的数据块对磁盘空间再次进行分割。但是我们想一个问题,磁盘空间一般是很大的,就算以4kb大小进行划分,以1T大小的磁盘空间为例,这样也会划分出很多很多的块,管理这样一个一个块还是很麻烦。于是文件系统将一大片数据块划分成一个区,在区中有划分很多组。每个组队若干数据块进行管理。我们想一想,如果若干块被一个组管理好了,那么是不是也就说明若干组也可以被同样的方式进行管理。如果一个区里的若干组被管理好了,那么也就说这个区被管理好了。同样的,我们用相同的方式管理其他组,这样若干个组就被管理好了。这些组都被管理好了,那么分区也就是被管理好,一个分区管理好,其他分区也用同样的方式管理。这样操作系统就管理好了好了整个磁盘空间。这就是一种分治的思想。那我们现在看看这个文件系统是怎么对磁盘空间进行区块划分的吧。这里磁盘区组块划分是在软件层面进行的。每个块都映射磁盘上的一块区域。

Liunx下的文件管理系统与软硬链接以及动静态库的制作使用理解_第3张图片

上述图中介绍了组内的超级块,组描述表,还有4个特别重要的概念没有介绍。在介绍之前我们先了解一个知识:在Liunx在中文件属性和文件内容是分开存放的。文件属性存放在inode节点中,文件内容存放在DatabBlocks中,每个inode节点都有唯一的编号。所以操作系统是通文件inode节点来确定文件的。这点很特别重要,它的结构大致如下图

Liunx下的文件管理系统与软硬链接以及动静态库的制作使用理解_第4张图片

在了解上述概念了以后就,我再来介绍组内剩下的4个部分:文件内容是放在数据块中,对于想存储文件内容就要1到n个数据块。如果想存储更多的文件内容就要更多的数据块,这个Data_blocks是组内专门用来存放文件的内容的数据块集合,用来表示一个组可供支配使用的数据块。一个组内肯定不止存放一个文件内容,那么也就说一个组会有多个inode,所以这个inodeTable是用来存放组内所有的inode的集合,也就是存放的都是组内可供使用的inode编号。一个分区的中inode编号是有限的,每个组内都有一个inodeBitmap来判断组内的inode节点使用情况。这个inodeBitmap中每个数据都是位图结构,一个比特位映射一个inode节点编号,如果该节点编号已经使用了,对应的位图位置的比特位就为1,如果没有使用或者某个文件被删掉了,那么位图中对应位置的比特位就为0。同样的每个组内的DataBlocks也是有限的,这个BlocksBitmap就是用来判断组内DataBlocks的使用情况的,它同样的也是位图结构。这样我们大致就了解了系统对磁盘管理方式。

基于上述知识,我们可能会有一下几个问题

1.文件系统是怎么分区的,又是谁分区的,什么时候分区的?

之前我们不是提到一个概念磁盘的逻辑地址,我们像用虚拟地址映射物理地址那样,可以用这个逻辑地址来映射真实的磁盘物理地址。有虚拟地址就可以用划分边界,划分边界就是分区。大致做法如下图;

Liunx下的文件管理系统与软硬链接以及动静态库的制作使用理解_第5张图片

当我们启动电脑的时Bootblocks被加载到内存中同时也会加载物理设备的驱动,之后操作系统就会对磁盘分区,分区之后进为了让分区正常使用就会对分区进行格式化处理,所谓格式化就是操作系统向分区写入文件系统管理信息。我们电脑上的C盘D盘就是系统分区。

2.文件inode和文件名对比

之前说过,操作系统以文件inode来标识一个文件节点,那么现在我们思考一个问题那文件名是的作用是什么呢?其实这个问题很简单,文件名是给系统看的,操作系统只认文件inode编号,操作系统通过inode文件编号定位一个文件。注意一点文件名并没有存在inode节点中。关于这点后续软硬链接中会提及。

3.目录是文件吗?如果目录是文件,那么目录的文件内容是什么呢?

其实目录也是文件,目录的文件内容就是该目录下的文件和子目录。操作系统会为文件目录分配inode节点和DataBlocks,目录的数据块中会存储该目录下的文件名和该文件的inode编号,这个文件名和文件inode编号互为映射关系。当我们在查找某个目录下的文件时,操作系统在背后做的事就是先找到该目录的inode编号,通过inode编号拿到文件属性,从而拿到存储文件内容的数据块编号,在通过目录下的文件名拿到对应的文件inode编号,然后就定位到了一个文件。

Liunx下的文件管理系统与软硬链接以及动静态库的制作使用理解_第6张图片

4.再次理解文件在磁盘上的创建和删除

通过上述知识的学习我们来分析一下在文件在磁盘上创建和删除。当我们在磁盘上某个地方创建一个文件时,首先操作系统会定位在文件在磁盘上创建位置,也就是确定在该文件在哪个分区下的哪个分组下创建的,之后系统会在该分组下的inodeBitmap中查找看看哪个inode编号没有被使用,确定某个inode编号没有被使用再将对应编号的inode节点分配给该文件,同时还会继续查找该分组的BlockBitmap看看哪个数据块没被使用,如果查找到了就会为把该数据块分配给该文件,然后文件indoe和文件数据块建立联系,这样一个文件就创建好了。

Liunx下的文件管理系统与软硬链接以及动静态库的制作使用理解_第7张图片

删除文件也和上面是类似,我们根据inode编号找到文件的inode节点,找到之后我们在根据inode找到文件数据块编号,拿到数据块编号就在BolcksBitmap中将对应的编号的比特位置为0,之后在找到inodeBitmap中对应inode编号位置的比特位将其置为0即可。这样就完成了文件的删除。这里我们就知道了删除文件是不是真正意义上的物理删除,通过改变bolcksBitmap和inodeBitmap中的编号对应的比特位来进行删除。这样也就是解释了当我们下载一个大文件的时候可能需要几十分钟,但是删除文件的时候就用几分钟就可以了。下载文件是真的将数据写入磁盘,但是删除不是真的将磁盘中的数据进行物理删除。如果我们误删了某个文件后,是可以通过一些技术手段来恢复的。前提是什么都不要动不要创建新文件,因为创建新文件的话,可能之前删掉的文件的inode就会分配给新文件,删除的文件的数据块可能被新文件使用,原有的数据可能会被新文件的内容给覆盖了,这样就恢复不了文件数据了。

Liunx下的文件管理系统与软硬链接以及动静态库的制作使用理解_第8张图片

在Bitmap中将对应编号位置的比特位置为0了以后,如果磁盘上要增加新文件这些indoe节点和数据块就会分配给新文件,inode节点编号和数据块编号保持不变,原有的文件属性和文件内容都会被新文件的属性和内容给覆盖掉。这样就把文件很好的管理起来了。inode编号可以确定分组,inode不能跨分区使用。

5.直接索引和间接索引

我们前面知道了inode节点中数组用来保存对应存储文件内容的节点编号,但是现在问题就是假如现这个数组开的最大容量是num[20],拿不就是只能保存20个数据块的编号,这20个数据块能保存的文件内容为20*4KB=80KB,那要是有个大文件好几百M怎么办呢?其实文件系统有两套机制来保存数据块索引,分别是直接索引和间接索引。看看下图。

Liunx下的文件管理系统与软硬链接以及动静态库的制作使用理解_第9张图片

这里因为作图的水平不太好,所以画图不是非常准确,但是大体意思是没啥问题的,都给出了必要的文字说明。这里多级索引虽然感觉好像浪费了一点数据块空间,但是这样就可以存储大文件的内容了。权衡利弊之后这还是一种不错的方案。

6.有没有一种可能一个组内的inode节点使用完了但是DataBlocks没有使用完,或者是DataBlocks使用完了但是inode没有使用完。

确实是有种可能,如果我一直新建空白文件,那不就是前者的情况,如果我新建了一个文件不断的往里面写内容,那就是后者的情况。出现上述情况后一般没啥好的解决方案,因为系统不能控制用户的行为,用户的行为是不确定的,文件系统被是已经被写好的软件,已经是固定死了处理执行方式。

通过上述我们知道操作系统是怎么管理磁盘文件的,当一些问题难以解决的时候在软件和硬件再加一层软件层,可能就很容易解决了,这也是软件工程常用的手段之一,也是计算机软件魅力所在

Liunx下的文件管理系统与软硬链接以及动静态库的制作使用理解_第10张图片

ls指令假上i选项就可以查看当前路径下文件inode节点编号

4.软硬链接

当我们需要访问路径很深的文件时就会用到所谓的软硬链接。我们知道真正找到磁盘上文件的并不是文件名,而是inode。 其实在linux中可以让多个文件名对应于同一个inode这就是硬链接,软链接是通过名字引用另外一个文件。

1.软链接

我们先来看一个简单的软连接示例

Liunx下的文件管理系统与软硬链接以及动静态库的制作使用理解_第11张图片

使用指令link -s前面是源文件 后面是要建立的链接文件。这个软链接文件是有自己的inode编号的,所以是一个独立的文件,有自己的inode节点和数据块。这个软链接文件的内容存放的是源文件的所在的路径地址。假如test.txt是可执行程序,我们执行这个t-link软链接和执行可执行程序的效果都是一样的。这个软链接和Windows下的桌面快捷方式是类似的,这样就可以很方便的在当前路径下执行或查看其他路径下的另一个文件。

在这里插入图片描述

link -s 源文件名或者源文件路径  链接文件名

删除软链接

unlink 后跟软链接路径

2.硬链接

同样的再来看一个硬链接示例

Liunx下的文件管理系统与软硬链接以及动静态库的制作使用理解_第12张图片

link 源文件或者源文件路径 链接文件名

这里使用指令ln不加选项后面依次跟源文件名或路径 链接文件名即可。这里的硬链接的inode节点编号和源文件的inode节点编号是一样的。也就说t2-link和t2.txt共用一个inode节点和数据块。这里的硬链接是新文件名和旧有的inode节点建立映射关系。所以t2-link和t2.txt是同一份文件只是文件名字不一样。那这是怎么做的呢?看看下图

Liunx下的文件管理系统与软硬链接以及动静态库的制作使用理解_第13张图片

这是通过引入计数的方式做到的,关于引用计数我在C++的博客中提到过。每次增加一个链接时计数就会加加一次。如果删除一个链接或者inode编号对应的文件时,引用计数就会减减一次。直到引用计数为0的时候,操作系统才会把inode位图和数据块位图对应位置的比特位置为0,完成文件的删除。我们使用ls -i选项的时候这个硬链接的引用计数也会显示出来。

Liunx下的文件管理系统与软硬链接以及动静态库的制作使用理解_第14张图片

有了软链接,那这个硬链接有什么用呢?我们在看下图。

Liunx下的文件管理系统与软硬链接以及动静态库的制作使用理解_第15张图片

这个空目录dir引用计数是2,为啥是2呢?之前我们知道.代当前路径..代表上级路径。所以dir是inode节点编号是1581420,另一个代表dir的.文件的inode节点是也是1581420。这个.文件就是硬链接指向dir目录。因为dir下没有其他子目录,dir的引用计数是2,如果dir还有一个子目录那么dir的引用计数就是3,因为还有一个..的硬链接指向dir.

Liunx下的文件管理系统与软硬链接以及动静态库的制作使用理解_第16张图片

通过上图查看确实如此的,但是我们不能为目录建立硬链接。系统不允许这样做,这样做了之后会造成路径回环问题无非定位文件路径。那为啥系统中会有. ..这样的硬链接呢?这是由系统自己定义了,系统会识别 . ..这种硬链接进行单独处理,系统是不允许用户自己为目录建立硬链接的。

补充ACM时间

这3个时间之前就提到过,这里在再简单提及一下。Access 最后访问时间 Modify 文件内容最后修改时间 Change 文件属性最后修改时间。这个文件内容被修改了,一般Change也会变,比如我们删除或者增加文件内容的时候文件大小会改变,也就是属性会改变。我们对文件最多的就是访问操作,所以现在Liunx下Access时间不会实时刷新,因为这种频繁的刷新,系统内存和外设需要进行频繁的IO交互会影响整机的效率。


5.动静态库

动静态库的概念之前提到过这里将对动静态库进行细致的介绍。我们知道在平时写C/C++代码的时候就一直在C/C++库。那到底什么是库呢?我们知道如果我们想用别人写的方法实现的代码,可以直接拿别人的源代码添加到自己的代码中即可。如果别人不想让你看到源码呢?还有一种方式就是别人提供给你方法实现的头文件,之后在将源代码编译成.o文件打包给你,就可以正常使用了。同时你也看不到别人的源码实现。C/C++程序被编译的时候会经历预处理,编译,汇编,链接,这4个阶段最后生成可执行程序。提供头文件和编译好的汇编后的二进制代码文件最后在链接的时候照样可以链接成功。这其实就是我们使用库方式,官方写好库,我们在下载vs的时候会将官方的库和相关的头文件都会拷贝到系统相应的目录下。我们在调用cout cin等语言库的方法时候,就是在使用官方库。库可以简单理为官方提供的方法实现二进制汇编文件,库的配合头文件一起使用的。语言之所以有库是为了提升开发效率,对于一些通用的基础的功能官方提供了实现不用重复造轮子。我们在使用编译器写代码的时候编译器会有语法提醒功能,就是因为编译器会在头文件中不断搜索,在没有编译的时候就会出现波浪号之类的语法提醒也是编译器根据头文件进行语法检查。

这个.o文件的全称是可重定位目标二进制文件

动态库后缀.so 静态库后缀是.a;库的命名方式是:以lib开头后跟库名最后加上后缀。当然有时候有些库会写上版本号比如C/C++库

在这里插入图片描述

1.静态库的制作与使用

Liunx下的文件管理系统与软硬链接以及动静态库的制作使用理解_第17张图片

上图中先将add.c文件编译形成.o文件,之后使用ar 指令将其打包成libmytest.a静态库,之后编译形成可执行程序的时候必须指明库名 库所在的路径。-L后面跟的是库路径,l后面的跟的库名,这里要注意的是库名要去掉前缀lib,去掉后缀才是真实库名。

ar -rc 库名 后跟.o文件
或者 ar -rc 库名 *.o//将当前路径下所有.o文件打包成库

但是实际上我们使用第三方库过程并不是这样的,一般真实的方式是别人会把实现好的第三方库文件和头文件打包成压缩包,其他人想使用这个第三方库的时候需要把压缩包下载到当前项目的路径下进行解压,并且把库引入到项目中。下面我将模拟这个过程。

Liunx下的文件管理系统与软硬链接以及动静态库的制作使用理解_第18张图片

cp *.a 目录名1
cp *.o 目录名2
tar -zcf 压缩包名.tgz 目录名1 目录名2

首先我们将.o文件拷贝进行include目录下,将我们制作好的动态库拷贝进mylib目录下,并且将其打包成myadd.tgz压缩包。这是只是第一步模拟他人上传的第三方库。

Liunx下的文件管理系统与软硬链接以及动静态库的制作使用理解_第19张图片

这里模拟的是将压缩包下载到当前项目的路径下并且解压,之后在当前路径的项目程序引入第三方库的库的方法和头文件。

Liunx下的文件管理系统与软硬链接以及动静态库的制作使用理解_第20张图片

gcc -o可执行程序文件名 项目代码文件名 -I后跟第三方库对应头文件路径, -L后跟库路径 -l后跟库名

这里就是使用静态库编译代码,-I选项指定头文件所在的路径,后面-L -l同样的是指定库所在的路径和库的名称。所以在使用第三方库的时候如果没有库的头文件和库文件放置在系统默认搜索路径下,就必须指明头文件的位置 库文件的位置和库文件的名称。但是实际使用库的过程中,一般也不常用这种方法,一般是将库的头文件和库文件添加到系统默认路径下。

Liunx下的文件管理系统与软硬链接以及动静态库的制作使用理解_第21张图片

我们将库对应的头文件和库文件添加到系统默认搜索路径下,这是需要sudo进行指令提权。库文件默认搜索路径是/usr/lib64/,头文件默认搜索路径是/usr/include/,添加到默认路径下后我们就不用指定路径了,但是需要注意的是我们使用的第三方库编译器不认识,所以必须指明库的名称。这里把库相关文件拷贝到系统特定目录下就是安装库。

2.动态库的制作与使用

上面介绍了静态库的制作与使用,这里介绍一下动态库的制作与使用。

Liunx下的文件管理系统与软硬链接以及动静态库的制作使用理解_第22张图片

gcc -c -fPIC 源文件
gcc -shared -o 动态库名 可重定位二进制文件

这里在生成动态库的时候就不需要别的指令了,直接使用gcc就能生成动态库,也可以看出动态库才是主流使用的库。制作动态库时gcc生成可重定位的二进制文件时需要加上-fPIC指令选项生成与位置无关码。这个与位置无关码下面会解释,这里先不着急。之后得到了.o文件,我们使用gcc -shared 指令将.o文件打包成动态库即可

Liunx下的文件管理系统与软硬链接以及动静态库的制作使用理解_第23张图片

export LD_LIBARY_PATH=$LD_LIBARY_PATH:路径

配置环境变量

这里在使用动态库的时候,需要注意一下.静态库是直接将代码拷贝到目标可执行程序中,但是动态库不是这样的,系统会在默认搜索路径进行查找第三方动态库,如果没找到还是链接不成功。这里先介绍第一种方法配置环境变量,将动态库所在的路径加入环境变量LD_LIBBARY_PATH中。但是这是一种临时方案,因为每次重新登录后环境变量就没了。不推荐,下面将介绍第二种方式。这个ldd指令是查看可执行程序所依赖的动态库。

建立软链接接

Liunx下的文件管理系统与软硬链接以及动静态库的制作使用理解_第24张图片

我们在系统库路径下建立一个软链接指向动态库的所在的路径位置即可。

配置文件
Liunx下的文件管理系统与软硬链接以及动静态库的制作使用理解_第25张图片

sudo touch /etc/ld.so.conf.d/mylib.conf
sudo vim /etc/ld.so.conf.d/mylib.conf
sudo ldconfig

还有第三种方式配置文件,系统/etc/ld.so.conf.d/路径下都是配置文件,配置文件可以自己命名,文件内容都是路径。我们在该路径下创建一个mylib.conf文件,将我们制作的动态库的路径添加到该文件下。最后需要更新一下配置文件让配置文件失效,使用ldconfig指令即可.


3.动静态库的加载过程

静态库链接
Liunx下的文件管理系统与软硬链接以及动静态库的制作使用理解_第26张图片

静态库链接生成的可执行程序文件中本身就有静态库中方法的实现,所以删除静态库程序照样可以运行,但是会造成资源浪费代码体积膨胀等问题。

动态库链接

Liunx下的文件管理系统与软硬链接以及动静态库的制作使用理解_第27张图片

动态库的加载过程就需要详细解释了。动态库链接的时候实际上是项目程序代码文件中调用动态库的方法的地方替换成动态库中的对应的符号。就是将来自外部符号进行替换,库中的方法是经过预处理 编译 汇编形成可重定位二进制代码。比如:cin方法在库中是某个特定的符号地址,链接的时候将cin替换成这个符号地址就行了。当我们运行程序的时候,系统会把该进程依赖的库也加载到内存中。程序成为进程,进程共享区中存放的是动态库中方法实现相对与库中代码起始位置的偏移量。 库一旦被加载到内存中那么它的物理位置就定下来了,这个时候共享区和和库的物理地址建立页表映射关系。当进程执行到cin地址符号的时候跳转到共享区进行查找,查找的时候根据偏移量就能找到cin方法的位置进行调用。系统会记录库中代码的起始位置,所以这个页表关系也能建立起来。这个共享区存放的其实是个相对位置。

为啥共享区存放的是动态库中方法实现相对与库中代码起始位置的偏移量

首先每个进程的运行的状态是不一样的,共享区的空闲位置不是一样的,每个进程不确定依赖多少个库。所以对应每个进程来说不能以一种统一的方式将确定共享区哪个位置可以存储库方法地址。采用这个存储动态库中方法实现相对与库中代码起始位置的偏移量的方式,可以保证无论这个偏移量存储在共享区哪个位置最后都能找到库中方法的实现。这样就是之前在打包动态库的时候提到的与位置无关。

同样的,我们也理解了为啥删除动态库后,依赖动态库的程序就无法运行了。这是因为当程序运行的时候通过共享区找到不到方法实现了,就会保错。我们有其他程序或者多个程序也依赖这个库的时候系统只会加载这库的一份方法实现到内存中 ,其他进程也是通过共享区找到方法的实现。所以使用动态库的代码体积不会膨胀,节省空间资源。但是对动态库的依赖性非常强。

以上内容如有问题,欢迎指正,谢谢!

你可能感兴趣的:(Liunx操作系统,linux,学习,文件系统,动静态库,软硬链接)