文章目录
- 浅谈文件系统
- 了解EXT系列文件系统
- 目录与inode的关系
- 软硬链接
- 动静态库
浅谈文件系统
当我们创建一个文件时由两部分组成:文件内容+文件属性,即使是空文件也有文件属性
一个文件没有被打开是存储在磁盘中的,而磁盘是计算机中的一个机械设备(SSD、FLASH等等)
以机械硬盘来举例
我们的数据都放在盘片上,磁头可以摆动,盘片不断在转动,这样磁头就可以定位到盘片上的特定位置。在盘片同心圆的一圈上有很多小段区域,称为扇区,它存储着数据,这一圈称为一个磁道,在同一圆心上的盘片的相同半径上的磁道合起来形成一个柱面。
磁盘写入的基本单位是扇区,一般一个扇区是512字节;磁盘和OS进行数据交互时的基本单位是4KB(具体和内存以及IO交互效率有关)
我们可以把磁盘想象成线性结构(如磁带那样)
可以把磁盘看成一个大数组,把扇区中的地址与数组建立起映射关系,并以LBA记录数组下标
若要与磁盘数据交互,需要把LBA转化成扇区地址
了解EXT系列文件系统
一个磁盘是很大的,那就需要对其进行管理,可是直接管理整个磁盘是很费时费力的,例如一个老师管理一个年级,那可能连人名都记不全,所以为了方便管理,就把磁盘分区管理
例如我们电脑中的磁盘分区,所以要使用磁盘具体可以分为两步:
- 把磁盘划分为小空间(磁盘分区)
- 格式化,给分区写入文件系统
例如学校校长->各班班主任->班长->组长->学生,所以再分区的基础上 ,又细分为很多小块区域(block group)组成
站在OS角度,每个区文件系统都一样,只要能管理好一个区,就能管理好整个磁盘
下面就以一个Block group来理解这个文件系统
在一个Block group中,Data blocks和indoe table中又分别由小块空间组成,每个小块空间又有自己的编号,文件内容存在Data block是中,文件属性存在indoe table中
那属性怎么找到内容所在位置呢?
首先我们需要知道的是,在Linux中文件名都是给用户使用的,实际的文件都有一个inode标号,而内部找到一个文件都是通过inode标号来找到具体文件的
inode table里面有很多数据块,每个数据块称之为一个 inode(有对应的 inode 编号),存放文件属性。
每个inode table数据块中存在着一个struct inode记录着文件的所有属性信息和Data中 blocks所占的数据块的编号
所以最终我们只要根据inode编号找到inode属性,根据inode属性找到对应的数据块就找到了整个文件
那么申请数据块时怎么知道哪个占用哪个没被占用?
中途有删除也不可能像顺序表一样把整体数据挪动所以这里用位图来解决这个问题,Block bitmap和inode bitmap分别代表Blocks的占用情况和inode的占用情况
(位图这里就不介绍了)
一个bit位用0记录空,用1记录占用,只需要对bit位判断是0还是1就判断了 inode和blocks使用情况
block group总结:
- Data blocks:由多数据块组成,每个数据块称之为一个 block,存放文件内容
- inode Table:由很多数据块组成,每个数据块称之为一个 inode,存放文件属性。
- inode Bitmap:记录inode数据块的占用情况
- Block Bitmap:记录blocks数据块的占用情况
- Group Descriptor Table:与 Block group 相关,描述这个组的相关信息。
- Super Block:记录整个分区的使用情况,这么做可以预防因为磁盘部分损坏导致的数据丢失,假如整个分区只有一份Super Block,万一磁盘损坏的部分正好包括这部分,那等于整个磁盘的数据就丢失了,所以每个Block group都有可以降低数据丢失的风险
目录与inode的关系
实际上目录也是文件,也有自己的数据块,目录数据块本质上是一张表,是inode编号与文件名的映射表,所以目录数据块存放的是文件名和inode的映射关系
(当前目录)
查看一个文件时的流程是什么?
cat temp.c
先查看/home/awd/test6中 该文件名对应的映射inode编号 ->
通过inode编号查看inode table中的文件属性 ->
通过文件属性找到Data blocks存放的文件数据
那么删除一个文件的流程是什么呢?
rm temp.c
先通过文件名在目录下找到对应的inode编号 ->
用inode编号 在block、inode的bitmap中把对应的映射位的1置0即可
数据内容根本不用动,我们看到的剩余空间容量多少多少实际是,根据占用位置描述的,也就是系统让我们看到的只是描述的剩余多少多少空间占用多少多少空间,创建新的时候覆盖就可以了,之后再更新空间描述的使用情况。真要是清空数据也不利于效率,毕竟总容量就那么大,删除也不会真扩容。
当我们touch一个文件会发生什么?
touch temp.c
先在位图中查看 block和inode占用情况 ->
找到空位置写入(覆盖)自己的文件的属性和数据 ->
在当前目录记录inode编号与文件名的映射关系
在我们windows中的回收站其实就相当于一个目录,只是把要删除的文件inode转移到回收站这个目录下建立映射关系,本质在磁盘中的位置并没有改变,而清空回收站才是删除(类似于把位图中的映射位置置空)
剪切也同理,就是更改目录
软硬链接
先来看软连接的语法
ln -s log.txt los_s 创建
unlink los_s 删除
当一个路径过长时,就可以使用软连接,类似于C++中的auto,或者windows中的快捷方式
软连接有独立的inode,是一个独立文件,有自己的inode属性和自己的数据块,数据块保存的是指向的路径+文件名
(例如C语言中指针指向的变量和指针变量他都是变量都有自己的地址)
硬链接语法
ln log.txt log
硬链接不是独立的文件,没有独立的inode号,而是一个别名(本质是根据原文件inode号,硬链接新的名字建立的映射关系)
指的是有多少个文件名建立的映射关系,这个数字存在inode结构体中
也就是引用计数,有一个名字建立映射就++,删除一个就–
当我们查看目录时的 .
..
就是一种硬链接
.
对应着当前所在目录,上图当前目录是test16文件中,所以与test16文件的inode号一样..
对应着当前目录的上一层目录,也就是test16的上层目录,就是awd,所以和awd的inode号一样
查看具体属性:
可以输入 stat + 文件名
查看具体属性
动静态库
一般库分为两种:动态库和静态库
静态库:将所有源文件生成目标文件(.o),再通过 ar 工具将目标文件打包得到静态库文件
动态库:在生成.o文件时还需要添加额外参数 -fpic /-fPIC,打包时添加shared参数告诉编译器生成一个动态链接库
具体步骤下面介绍
在Linux:
- 动态库以.so作为文件后缀
- 静态库以.a作为文件后缀
库文件的命名:
- 动态库:libxxxx.so
- 静态库:lib.xxx.a
库真实的名字:去掉lib前缀,去掉后缀,剩下的就是库名称
gcc一般默认是动态链接编译,想要静态链接编译可以在编译时加 -static
静态编译链接
静态编译链接是把库中需要的的代码拷贝到可执行程序中,所以静态编译的文件一般比较大,因为包含了库文件
动态编译链接
如何制作库:
库本身是由二进制文件组成的,一般一套完整的库有一下三点:
- 库文件本身(.o/.obj的合集)
- 头文件.h
- 说明文档
静态库:
- 把所有的源文件生成一份.o/.obj文件
- 把所有的.o/.obj文件打包成一个库文件,要根据上面介绍的命名规则
- 编译链接
ar -rc 静态库名称 .o文件名称
命令的作用是把 .o 文件打包形成一个静态库。使用静态库:
这里我把output剪切到上一目录改名为SRAM了
temp.c #include "sub.h" //#include "./file/output/sub.h" #include "add.h" //#include "./file/output/add.h" int main() { int x = 20; int y = 10; int ret1 = sub(x,y); int ret2 = add(x,y); printf("add:%d\n",ret2); printf("sub:%d\n",ret1); return 0; }
- -I(大写i):指明头文件搜索路径
- -L:指明库文件的搜索路径
- -l(小写L):指明要连接哪一个库
我们平时写的代码不用指明搜索路径是因为,这些库都放在默认路径下:/lib64,/usr/lib,/usr/include等。所以也可以把自己的库放到默认路径下(但不推荐)
动态库:
动态库的制作与静态库基本一致
打包.o文件生成动态库,此时的库后缀应该是.so,但一些选项和静态的不一样:
gcc -shared -o 动态库名称 .o文件名称
生成一个动态链接的共享库。- -fPIC:表示生成位置无关码。
使用动态库:
编译时指明路径和库名
但是编译通过了,也生成可执行文件了,可是运行时就报错了,为什么呢?
因为动态库不光编译连接时需要找到动态库,再运行时也需要找到动态库,所以我们要让他运行时也能找到库
做法
利用环境变量 LD_LIBRARY_PATH
把库的路径放到环境变量中,但只限于本次会话
- 把库加载到默认路径下(不建议)
动态库与静态库制作及使用的区别:
创建时:注意后缀,ar -rc 、-shared -fPIC
使用时:动态库需要把库路径放到环境变量中
还要注意:指明文件路径和库的名称
制作库的步骤:
把源文件生成.o文件
把所有.o文件打包成库(根据动静态,选择后缀和选项)
把库文件和所有头文件打包发布
最后编译连接时注意动静态的区别和文件路径
注意:上面说的是动静态库,不是动静态的编译链接,gcc/g++默认是动态链接,静态链接需要手动添加 -static选项
本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。
Windows下是.obj文件,Linux下是.o文件
-
静态库的特点:
-
在程序编译的链接阶段,静态库的代码就被拷贝进了程序中,所以程序在运行时不需要找静态库。
-
静态库的代码在编译过程中已经被载入可执行程序,因此体积较大。
-
当静态库更新时,它所有的程序也需要更新,因为是和原来静态库的代码编译在一起的,很可能因为库中小改动导致改动整个程序(当库更新时,等于原库文件发生改变,而程序中拷贝的是原库的代码,所以需要重新编译链接)
-
-
动态库的特点:
-
相比于静态库的运行速度较快,程序在运行的时候才去链接动态库的代码,所以在运行时也需要找到动态库
-
动态库(共享库)的代码是在可执行程序运行时才载入内存的,在编译过程中仅简单的引用,因此代码体积较小。
-
对于更新发布时,不会像静态库那样从上到下全改动,一般只需要更新动态库即可(一般库的更新不会改变原有的逻辑与接口,只会是优化和新增功能,而动态库是在程序编译时才链接库,所以只需要更新库即可)
-
用静态库分别去动态链接和静态链接都可以编译,但是动态库只能动态链接