Linux基础IO(三)

磁盘的存储结构

磁盘的物理结构

扇区: 每个扇区512字节,也是磁盘读写的一个基本单位。
在这里插入图片描述
物理上将数据写入到指定的扇区方式
确定写入的数据在哪一个面上( 对应的便是哪一个磁头) ——> 确定哪一个柱面( 磁道)——>确定哪一个扇区。
简称CHS寻址方式,如果我们有了CHS寻址方式,就可以找到任意一个扇区。

磁盘的抽象结构

将500G的磁盘抽象为一个数组,每个单位的大小为500字节。将来要答访问一个扇区,我们只需要知道数组的下标。 这种寻址的方式就叫做:LBA。(location block adress)

所以将来我们想要访问磁盘的某个扇区,秩序需要将通过LBA寻址后转换为CHS物理寻址就行了。
在这里插入图片描述

LBA转换为CHS

假设将数组抽象为100000各数据元素,前10000多个元素属于磁盘的第一面,1000到2000个数据元素属于第二面,在每个面上,前1000个数据元素属于第一个磁道,1000到2000个数据元素属于第二个磁道,一个磁道又分为若干个扇区。
此时对磁盘的管理就变成了对数组的管理。
为了节省成本进行管理,我们可以加个你500G分为各个大小不同的空间。
在这里插入图片描述
最终:对磁盘的管理就变成了对一个个小分区的管理。

磁盘分区结构

例如: 当我们对100GB的分区进行管理时,我们可以将分区分区为n个Block group 和一个Boot Block.
每一个Block group 都分为
在这里插入图片描述

Super block : 不属于Block group 中,它代表的是整个磁盘大分区的属性集,对应的是这个分区一个有多少个块组(Block group),每个块组的使用情况等信息。

为什么每一个Blcok group组中都有一个分区 Super Block呢?

为了防止磁头刮花磁盘,所以每一个Block group中都有一个Super Block 备份文件信息。

Data Block : 多个4KB个(扇区大小*8) 的集合,因为Linux在磁盘上存储文件的时候,是将内容和属性分开进行存储的, 所以Data Block 保存的是特定文件的内容。

inode Table: 指的是 该块组内所有文件inode 空间的集合。

inode:指的是 一个大小为128字节的空间,保存的是对应文件的多种属性,例如: 文件的权限,文件的大小,文件的修改时间等,并且每一个inode 块 都具有一个inode编号。

BlockBitmap: 假设有10000+个blocks 就有 10000+个比特位,: 比特位和特定的block是 一 一对应的,其中比特位为1,就代表该block被占用,否则就表示可用。

inode Bitmap: 假设有10000+个inode节点,就有10000+个比特位,比特位和特定的inode是一 一对应的,所以就变成了Bitmap中比特位为1,就代表该inode被占用,否则就表示可以可用。

这两个位图管理block 和 inode 的使用情况。

GDT: 块组描述符,表示块组的使用信息。例如:描述块组多大,使用量,有多少个inode,占用块组空间内存。

总结
我们将块组分割为各个分组,并且写入相关的管理数据——>
n个块组——> 整个分区就被写入了文件系统信息——>磁盘格式化

哪些Data block属于同一个文件?找到目标文件的inode编号,怎么找到文件的内容?

我们知道一个inode对应一个文件,一个inode中有一个blocks数组,这个blocks数组中的元素对应这文件的Data block 。当我们拿到inode编号,就可以找到inode,通过block数组应映射到同一个文件中的Data blcok ,进而找到对应文件的文件内容。此时文件属性(inode)和文件内容(Data block)都具备了,最红便可以访问目标文件了。
在这里插入图片描述

当一个文件特别大时,怎么怎么处理文件内容呢?

在Data block 中,并不是所有的Data block 只能存文件数据,也可以存储其他Data block的块号,此时多个块的块号又可以类似二叉树的的方式与该文件的其他Data block 产生联系,所以一个Data blcok 就可以找到该文件的多个Data block。

如何找到inode呢?

我们要找到文件,必须先知道文件名——>找到特定的inode编号——> 知道到分区特定的block group ——>
找到inode table ——> 找到inode ——> 找到目标文件属性——> 找到blocks数组通过映射找到该文件对应的Data blocks。

文件名与inode

1:一个目录下,可以保存很多文件,但是这些文件不能又重复的文件名。
2:目录是文件,那么目录也可以有自己的inode和 Data block。

文件名在目录的Data block 中,它保存着与inode编号的映射关系,文件名与inode互为key值,都是唯一的。

为什么在目录里面需要w权限
因为在这个目录下创建文件时,这个目录有自己的数据块,我们创建文件的文件名就在目录的Data block 中,所以我们要将文件名和inode编号写入保存,此时必须需要w权限。
为什么在目录中具有r权限呢?
当我们需要显示文件名时,我们只能从目录的内容中获取文件名及相关属性,就必须访问目录的文件内容,比如要拿到myfile的文件属性,就必须找到myfile文件中的inode后才能到特定的文件属性,所以就必须需要r权限从目录的Data block中获取文件名。

创建文件中系统做了什么?


在目录中创建文件时,找到目录文件 ——> 找到特定的文件分区 Block group ——> 在inodeBitmap 中遍历图 ——> 找到第一个为0的比特位——> 说明它是没有被占用的内存——> 将比特位置为1——> 获取文件特定的inode编号——> 在inode表里面将文件属性写入——> 刚创建的文件没有内容,所以将blocks数组所映射的Datablock置为NULL——>后面要将数组进行写入的时候,写入到特定的Data block后与blocks数组建立映射关系。
二:
此时我们拿到了新创建文件的inode编号,而如果我们要在目录下创建文件,必须将文件名与inode编号建立映射关系——> 文件名从用户输入而来,inode由文件系统返回而来后建立映射关系——> 找到目录的inode编号及文件名后,将映射关系与文件名写入——>文件创造完成。

删除文件,系统做了什么?

删除文件肯定是在这个目录下删除——>找到这个目录的Data Block ——> 删文件用户提供文件名——在Data Block中索引查询由文件名进行映射的inode编号——> 找到将inodeBitmap对应的比特位由1置为0,将Block Bitmap中的比特位由1置为0——>在目录的Data Blocks中将文件名与inode编号解除映射关系——>文件删除。

系统怎么恢复文件?

找到被删文件的inode ——> 将inode特定Block Group 中的inodeBitmap进行恢复——> inode的使用情况就变为使用状态,就能了解inode属性——>通过inode寻找特定的该文件Date block——> 将该文件Data block 所对应的blockBitmap进行恢复为使用状态——>文件恢复正常。

注意
文件恢复前提: inode 和data block 没有被覆盖占用,如果我们删除文件后再进行文件操作,很有可能会将被删除文件的inode和Data block 覆盖——> 此时文件就很难恢复了。

查看文件,系统做了什么?

在内核对象中打开这个myfile.c文件——> 将数据写入到文件中的缓冲区后操作系统定期刷新到文件上——> 通过文件名在目录中的Data block 中找到对应的inode编号——>找到分区对应的block group ——> 找到对应的inode——>通过inode中的映射表找到对应的Data block——>将该文件的内容刷新到对应的Data block中。

系统还有空间创建文件为什么会失败呢?

inode和Data block都是固定的,可能inode编号和Datablock二者之一有缺失。

软硬件链接

软硬件链接的区别

软连接有独立的inode ——> 软连接是一个独立的文件

硬链接没有独立的inode ——> 硬链接不是一个独立的文件
在这里插入图片描述

软连接

例如:我们在路径bin/exe路径下创建了一个可执行程序mytest。
在这里插入图片描述
当我们在本地路径下运行时,运行该可执行文件一般要写明该程序路径。为了方便,我们可以将该可执行程序设为软连接,在面对该可执行程序路径很长时可以简便该操作,而打此时的软链接也可以默认是windows可执行程序的快捷方式。
在这里插入图片描述

硬链接

我们分别创建了同一程序的两个硬文件,然后通过 ll -i 命令对这两种硬文件进行inode 查看,发现他们有inode但是ino的和文件属性是相同的,这就说明他们是同一个文件。

在这里插入图片描述
在这里插入图片描述

创建硬链接时系统干了什么?

就是在指定目录下,建立了文件名与指定inode的映射关系,俗称为起别名

当我们删掉一个硬链接时, 会发现硬链接指向的文件并没有被删除,但是硬链接数由3变成了2.
在这里插入图片描述
在这里插入图片描述
在一个目录里面会保存着文件名与inode的映射关系,但我们建立多个硬链接时,inode怎么知道有多少个文件名与他相关联?

inode 里面有一个引用计数,创建一个文件时引用计数+1,当删除一个文件时,并不是将这个文件的inode删除,而是将inode 的引用计数–,当此时文件的inode的引用计数变为0的时候,就说明已经没有文件名与它相关联了,这个文件就说明被真正删除。

硬链接的用途

当我们默认创建一个目录,会发现该默认创建文件的引用就计数为什么为2呢?
因为自身目录名以及目录内部的 . , 都和该目录对应的inode具有映射关系。而. 也代表当前目录。
在这里插入图片描述

此时,当我们在boke1目录中又创建一个boke2目录,此时通过ls -lia 命令可以查看到inode引用计,说明文件名与inode关联关系又增加了1,当我们进入boke2查看boke2的…的inode 与boke1中的 . inode相同。这说明了boke2中的… 也是boke1的硬链接,…也表示上一级目录。
在这里插入图片描述
在这里插入图片描述
总结
我们可以通过引用计数-2 (一个是自身一个是.)就可以算出该目录中存在的目录个数了。

动静态库

静态库

为了操作方便,可以利用Makefile将.c文件统一形成.o文件,再利用ar-rc命令打包生成静态库libhello.a。
在这里插入图片描述
我们还可以利用Makefile文件将.h 文件和.o文件和静态库文件分别放入同一目录中hello的include目录和lib目录中,再使用cp -rf 命令将main.c和静态库hello放入userlib中供使用者使用。
在这里插入图片描述
在这里插入图片描述

静态库的使用方法

我们知道
头文件中Gcc的默认搜索路径是: /usr/include
库文件的默认搜索路径是: /lib64 or /usr/lib64

方法一
当gcc编译第三方库时,我们必须要告诉gcc编译库的头文件和库的位置,我们可以利用 sudo cp -rf命令将hello/include 里卖弄静态库声明全部复制在 /usr/imnclude 中
在这里插入图片描述
利用sudo cp -rf 将hello/lib/libhello.a里面的静态库全部复制在/lib.64中。
在这里插入图片描述
最后编译运行的时候要告诉gcc 需要编译的库为哪个库(去掉前后缀)才能将程序运行起来。
在这里插入图片描述
总结
最后编译运行的时候要告诉gcc 需要编译的库为哪个库(去掉前后缀)才能将程序运行起来。

方法二:
直接使用。
c语言先在当前路径下搜索然后在系统路径下搜索,而我们的头文件并不在当前路径中。
所以我们必须显性的告诉编译器头文件的路径,但是此时库文件也找不到。
所以也要告诉编译器在指定路径下搜索库文件 并且要指定路径中我们所需要的库(去掉前后缀)。
在这里插入图片描述

动态库

先利用gcc -fPIC 目标文件 -o 生成达.o汇编文件 ,再利用gcc -shared
命令打包生成动态库。
在这里插入图片描述
也可以利用Makefile同一生成.o文件,并统一打包成动态库。
.PHONY:all
可以一键生成动态库和静态库,不然make只会生成动态库。
并将额外动态库拷贝到output/lib命令中。
在这里插入图片描述
然后将output 放入userlib目录中,再利用 tar czf 命令将output打包,鄙人就可以通过安装包进行安装了。
在这里插入图片描述

动态库的使用

我们知道如果我们只有静态库,gcc只能针对该库进行静态链接,如果将可执行程序全部以静态库链接在命令后面加static.

如果动静态库同时存在,默认用的就是动态库。
在这里插入图片描述
可是,当我们运行动态库的可执行程序时,发现却无法运行。
因为我们在编译时显示标明动态库的路径是对gcc说的,但是我们在对静态库编译运行时应该额外对Linux系统表明。
在这里插入图片描述在这里插入图片描述
方法一
在LD _LIBRARY_PATH 环境变量中添加动态库的绝对路径后运行。
但是这个方法是当我们将linux关掉此时所增加的环境变量就没了。

在这里插入图片描述
方法二
在 /etc/ld.so.conf.d/ 目录中创建一个文件(后缀一定要是.conf)就可以了。

在这里插入图片描述
在这个文件中加入动态库的绝对路径。
在这里插入图片描述

然后再使用sudo lddconfig 更新就可以了。
在这里插入图片描述
方法三
软链接方法:
将动态库设置为 /lib64/的软连接,再设置软链接时最好以动态库的绝对路径设置,否则系统会找不到动态库的位置。
在这里插入图片描述
最后编译时告诉编译器动态库的头文件和位置,并且指名动态库就可以成功编译运行了。
在这里插入图片描述
例外,当我们设置软链接出错时可以使用sudo unlink 命令解除。
在这里插入图片描述

动态库与静态库的区别

静态库使用时是将可执行程序加载到内存中后通过页表映射到地址空间的代码区中,进程会在代码区来回执行,并且每一个进程运行该程序都会将该程序反复加载到内存中。

而静态库可以和可执行程序分批加载,当进程执行到可执行程序的动态库代码时,动态库便会加载到内存中,后通过页表映射到地址空间中堆栈之间的共享区,此时进程便会从代码区跳转到共享区,当有多个进程运行该执行程序的动态库时,此时加载到内存中的动态库会共享到每个进程,从而减少内存的浪费。

在这里插入图片描述

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

暂停更新

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值