理解文件系统与动静态库(Linux基础)

1. 文件系统的硬盘结构

之前学到,文件=属性+内容。而inode就存储这属性集合。而内容存储在block中,可能是多个block因为不确定文件内容的大小。硬盘上有很多文件,当inode和block多起来,就要被先描述在组织。同时也要对文件进行操作,所以就引出了文件系统。

filesystem
{
//基本情况
//空间一共多大
//已经使用和没有被使用
//inode
//block
//group
方法:
//创建
//删除
//等等
}

当我们ll时,文件呈现出如下形式:
在这里插入图片描述
每行包含7列,依次是:

  • 模式
  • 硬连接数
  • 文件所有者
  • 大小
  • 最后修改时间
  • 文件名

当在命令行敲下ls-l这条命令时就发生了如下操作:

在这里插入图片描述

其实这个信息除了通过这种方式来读取,还有一个stat命令能够看到更多信息
在这里插入图片描述
磁盘的结构如下:

在这里插入图片描述
硬盘上最小单位为扇区,扇区大小为512个字节,一个block大小为4kb,也就是说一个块拥有8个扇区。磁盘中的光片,两面可以写入。
操作系统读取扇区的时候是靠逻辑地址,所以在底层其实将他们换分成一个大数组,通过下标划分不同的区域。
而这分区还需要再划分成一个个块组(BLOCK GROUP)。每个块组的结构相同。
在这里插入图片描述

  • 超级块(Super Block):存放文件系统本身的结构信息。记录的信息主要有:bolck 和 inode的总量,未使用的block和inode的数量,一个block和inode的大小,最近一次挂载的时间,最近一次写入数据的时间,最近一次检验磁盘的时间等其他文件系统的相关信息。Super Block的信息被破坏,可以说整个文件系统结构就被破坏了。描述整个文件系统的信息
  • GDT,Group Descriptor Table:块组描述符,描述块组属性信息。具体某个分组的详细信息
  • 块位图(Block Bitmap):Block Bitmap中记录着Data Block中哪个数据块已经被占用,哪个数据块没有被占用
  • inode位图(inode Bitmap):每个bit表示一个inode是否空闲可用。1字节8比特位,1kb=1024个字节,1mb=100万个字节=800万个比特位,可以保存10万个位图,
  • i节点表:存放文件属性 如 文件大小,所有者,最近修改时间等.
  • 数据区:存放文件内容(数据)。
    找一个文件,需要先找到inode,在找到block(可能是多个)。
  1. 怎么找到Inode?
    每一个inode对应一个inodeid。

  2. 怎么找到BLOCK
    Inode里面存放着一个map数组,数组里面的指针指向一个个BLOCK,就可以找到Block.
    INODE里存放着属性信息,DataBlocks存放着数据信息。

  3. 怎么创建文件?
    创建文件,找到分区,块组,分配INODE,在inode位图里,找一个为0的位,然后将他置为1。通过这一个位找到inode表里的inode,将属性信息填入,然后在块位图里找一个未被使用的block的位(编号),通过这个位找到block,填入内容,然后将block的位(编号)写入到inode的map数组里。最后将文件名,inode的放到目录文件的block中,文件名和inode之间有着映射关系。

  4. 怎么找到文件?
    在struct file中存在path,path中有一个dentry,里面存着文件目录的inode,inode里面有和目录block的映射,目录的block存着文件名和inodeid,根据inodeid,在找到文件的inode。

  5. 怎么删除数据?
    将块位图的位,置成0。将inode位图的位,置成0。所以删除数据非常快

  6. 怎么恢复数据?
    通过某些手段或工具获取inode,根据map数组映射找到block将0恢复成1.

  7. 看上去我们每次都在遍历,实际上Group Descriptor Table里就整体描述了inode和block的使用情况。

  8. 目录也是一种文件,所以目录肯定也有inode存放着属性,那么目录里的block放什么呢?
    那么目录里的block实际存放着文件名和inode之间的映射关系。他inode都找到了,那么block也能找到。

2. 软硬链接

2.1 硬链接

ln    new.txt  h_link 

创建硬链接,可以看到h_link和new.txt的inode是一样的
在这里插入图片描述
当我们修改new.txt,h_link也会跟着修改
在这里插入图片描述
在这里插入图片描述
硬链接:和一个文件共享inode。将文件的inode和链接文件建立一个映射关系。

  1. 就像我们重命名的时候,本质就是创建一个硬链接文件,删除掉之前的文件,而且属性和内容是一模一样。
  2. 在这里插入图片描述
    当h_link和new_txt指向同一个inode的时候,硬链接数是2。
    当2021_4_7这个目录里都是文件,没有目录时,硬链接数是2。

他自己指向自己的inode算一个。
在这里插入图片描述
在目录里还有一个 . ,.代表当前目录所以这个文件名也指向了那个inode,所以一共是两个。

在这里插入图片描述
当我们在2021_4_7里在创建一个目录时,发现硬链接数变成了3。
在这里插入图片描述
其实是在我们刚创建的目录里,又有一个…指向了上级目录,也就是指向了那个inode。所以他自身文件名,本目录的.,
目录里还有一个目录,这个目录还有个..,都指向了inode,所以硬链接数就是3.
在这里插入图片描述

2.2 软链接

ln -s log.txt link

创建软链接,link是一个独立的文件,因为他的inode不一样。

在这里插入图片描述

软链接:具有独立的inode,是一个独立的文件。本质就是在创造文件的基础上,在block中存储log.txt的路径。
比如windows的快捷方式。

假如不想用环境变量的情况下,而他又在比较底层的目录中,想快捷的启动一个可执行文件。就可以用软链接。

3.动静态库

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

当编译完一个程序后,使用ldd命令,查看他的链接关系。
发现此程序默认使用的是动态库。
在这里插入图片描述
一般的命名方式是 lib+name+.so+version所以这个动态库是c库。
当使用静态链接生成的话(gcc后面加static),将近大了100倍,仅仅是编了一个hello world。
在这里插入图片描述

动态链接和静态链接运行时的区别?

  1. 动态链接:运行程序的时候,遇到printf函数的时候,才去链接动态库中的代码去执行。所以当他编译链接完程序没有库代码那一部分,是比较小的。
  2. 静态链接:当你gcc编译链接的时候,就把库的代码和源文件链接在一起在代码区中也就是说,一旦编译链接完,运行的时候就不依赖外面的那个静态库了,因为都在代码区所以编译链接完,程序比较大。

动态链接和静态链接的优劣?

  1. 动态链接:程序生成的体积小,程序运行起来成为进程,运行到进程中使用到库函数代码那一步时,动态库的代码才会加载到内存中。所以比较依赖库,物理内存中只有一份。哪个进程用,哪个进程的虚拟地址空间(共享区)通过页表映射到物理内存。
  2. 静态链接:程序生成的体积大,编译链接的时候就把对应的代码链接到程序中,一旦编译链接完,不需要依赖库。内存中有多份拷贝(多个进程,多个代码区,多个拷贝)

和之前的内容关联一下。

一个标准的第三方库是要包含两个东西。include头文件以及.a或.so
例如c语言提供的:
/usr/include/
usr/include
/lib64/lib.so
在这里插入图片描述

而一批头文件提供的是,有什么方法可以使用,接口参数的含义。
一个或多个库文件:就是方法的具体实现(二进制),供我们进行动静态链接

3.1 模拟静态链接

先编写好.c和.h文件,因为静态链接是在源文件链接的时候,从静态库直接拷贝代码放在代码区,意味着当你的代码被转换成二进制的时候才会拷贝静态库的代码,所以我们要把.c文件写好,并编译链接成.o文件,才能模拟出静态链接的效果。

将所有的.o文件打包,变成了静态库。别人用的时候只需要包含头文件,然后找到链接写好打包的静态库。

ar -rc libmymath.a MyAdd.o MySub.o

其实这就是我们平常使用人家的库函数,先包头文件(声明),然后链接的时候就从静态库里拷贝他.o形式的实现代码
mytest.c代表测试静态库的例子。(等会把makefile,mylib一换还可以测试动态库的例子)
在这里插入图片描述

模拟c库中的分布,在include下包含.h文件,在lib目录下包含库文件

在这里插入图片描述
由于人家c库的环境变量里面包含了头文件和库文件的路径,所以直接包含就可以找到。
(编译的时候找声明文件,链接的时候找静态库的二进制实现文件。)

我们要模拟只好在makefile中用自带路径。

I代表include头文件所在位置
L代表lib库文件所在位置
l代表库文件的名字
-I ./mylib/include -L ./mylib/lib -l mymath

这样别人最终只需要,头文件和打包的库文件,就可以使用我们写好的方法。和我们使用c语言库的原理是一样的。

不带也可以,把头文件,库文件放在系统默认搜索路径,就不用带I,L但是库文件名字要带(配置一下也可以不用带)但是尽量不要污染环境。

3.2 模拟动态链接

在现在文件夹,创建一个dynamic文件夹,里面先编写好mylib。(到时候拷贝到外面就好,在改一下外面的mkfile,就能在演示动态链接的例子。)

.c和.h文件同样模拟C语言库的放置路径。只不过我们这次不再外面一个个敲命令建文件夹,直接在makefile文件写好

  1 libmymath.so:myadd.o mysub.o
  2   gcc -shared -o $@ $^
  3 myadd.o:myadd.c
  4   gcc -fPIC -c $<
  5 mysub.o:mysub.c
  6   gcc -fPIC -c $<
  7 .PHONY:clean
  8 clean:
  9   rm -rf output *.o  libmymath.so 
 10 .PHONY:output
 11 output:                                                                                                                                                             
 12   mkdir -p mylib/include
 13   mkdir -p mylib/lib
 14   cp *.h mylib/include
 15   cp *.so mylib/lib 

因为makefile是依赖关系,所以从第6行向上看。

  • -shared代表共享库,
  • gcc用到的参数fpic代表-fPIC与-fpic都是在编译时加入的选项,用于生成位置无关的代码(Position-Independent-Code)。这两个选项都是可以使代码在加载到内存时使用相对地址,所有对固定地址的访问都通过全局偏移表(GOT)来实现。-fPIC和-fpic最大的区别在于是否对GOT的大小有限制。-fPIC对GOT表大小无限制,所以如果在不确定的情况下,使用-fPIC是更好的选择。

主要看伪目标output代表我们创建的路径,库文件和头文件所在位置。
删除静态库例子(mylib),把生成的mylib(这个是动态库例子),拷贝到上级目录。
上级目录的makefile也要改一下,刚才加了-static,演示动态链接时需要去掉,路径那些不变。

接着make可以生成可执行文件,但是运行却出错,没有找到,为啥呢?
在这里插入图片描述

由于知道声明文件的位置,使用gcc编译是可以生成可执行文件的,编译器知道库在哪但没用到,因为动态链接是程序启动之后变成进程,需要的时候动态库对应代码加载到内存,这是由操作系统加载的,但是我们没有将路径告诉操作系统,操作系统不知道。

默认的在系统环境变量下,编译器操作系统都能找到,所以我们用这个命令,把路径加入到找动态链接的环境变量中

export LD_LIBRARY_PATH=/home/zjn/lessones/14_lesson/mylib/lib

再次运行文件就可以运行了
在这里插入图片描述

  • 11
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

楠c

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

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

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

打赏作者

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

抵扣说明:

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

余额充值