Linux学习之文件系统与动静态库

2024-01-29 04:52

本文主要是介绍Linux学习之文件系统与动静态库,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

一,文件的管理

什么是磁盘?

磁盘的逻辑抽象结构

格式化

inode

挂载

软硬链接

二,动静态库

什么是动静态库?

1.站在库的制作者角度

静态库:

制作一个静态库

2.站在静态库使用者的角度

动态库

作为制作者

作为使用者

 动态库加载


一,文件的管理

和进程,内存一样,操作系统创建它就要管理他,文件也是如此,也需要操作系统来管理,那么如何管理呢?还是我们的六字真言,先描述在组织,这与我们学习进程的思想是近似的。

而我们

管理文件的核心就是找到文件,即文件的路径,其次管理文件根据文件的属性分为两种:

1.打开的文件在内存中的管理。

2.未打开的文件在磁盘中的管理。

 所以我们首先第一件事就是先弄清楚如何找到文件的路经,并且是快速的,准确的。而了解这个,就先了解磁盘是如何存储并且定位寻找的。

什么是磁盘?

磁盘是一种用电磁原理记录数据的存储设备,由涂上磁性物质的盘片和盘片读写装置(驱动器)组成。磁盘可以分为软盘、硬盘等类型。如果您需要管理硬盘分区及文件、搜索丢失的分区及文件、对磁盘进行快速分区格式化等操作。磁盘价格便宜,容量大,是企业大量使用的存储介质。

磁盘的结构:

磁盘与盘面的比例是一比一的,通过磁头的摆动,来定位磁盘的某个位置,磁盘的摆动只能左右摆动,盘面是在高速的旋转着,相对于其他外设较慢。其中磁头是具有磁性的,受到碰撞就会损坏磁盘。

一个磁盘上面有许多磁道,他们是一圈圈的同心圆,而扇区就是把一个磁盘分成若干份,因此磁盘再进行存储时,扇区是磁盘被读写的最小单位,一般是512字节,当我们需改某一扇区的即使某一比特位是,也需要加载这个扇区到内存中,所谓一我们把磁盘这样的设备叫块设备。

那么我们如何寻址,定位,选择哪一个扇面,本质就是上选择磁头,选择好了哪一面,下来就是选择哪一个磁道,最后在磁道当中最后在来选择哪一个扇区。同一个磁道构建出的柱面,这种的寻址方法在这里(head,cylinder,sector)就是CHS寻址法。

而文件包括了属性加内容,因此在存储时,属性和属性放在一些,内容与内容放在一起,有的磁道访属性,有的磁道放内容。经过磁头找磁道,盘面旋转确定扇区,我们完成寻址。

磁盘的逻辑抽象结构

操作系统无法用CHS这种方法,因为软硬件是不能固定的,软件的寻址方式变化,硬件不可能要变,因此软件方面是不会使用这种方法的。

对于磁盘,盘面是由一个个同心圆构成,我们可以将该盘面抽象成我们小时候用的磁带一样,比如步步高学习机,在机子里,磁带被旋转卷成一个圆,我们就可以类比这个圆是盘面。

旋转的时候是一个圆,那么拉直的时候就是一个长条子,此时的扇面就是相当于磁带一段一段的,如下图:

一个磁道是一个数组,因此用二维数组我们就可以表示出一个盘面,对于任何某一扇区的位置,我们只需知道它的下标位置即可,通过已知的下标位置,由于一个盘面的大小我们是知道的,我们也可以计算出他是哪一个盘面中的第几个。比如小标123456,同盘面大小取模再取余就能知晓扇区的位置。

于是对磁盘的管理,就变成了对数组的管理。由数组下标就转化成磁盘的CHS了。当然操作系统以扇区为单位进行存取,也可以基于文件系统,按照文件块为单位进行数据存取。例如以8个扇区为一个文件快,直接一起写,在写时也是连续的。因此我们能通过起始地址找到八个块的所在地。

最终结论:对存储设备的管理,在os层,转换成了对数组的增删查改。

格式化

在内存管理方面,我们只需要管理其中一部分空间,剩余的由于与值顺序链接的,我们就可以管理这以快空间。

以分治的思想,将一个大的分区划分成一个个小的块,进行管理,最后将管理中心放在一块小空间即可。

而我们需要知道的文件的路径取决于就是我们对这些组做管理,文件的存储,分为两个部分,一个是文件信息的存储,一个是管理文件信息的存储。因为文件的内容与属性是分开存储的,因此文件信息管理的存储决定了文件信息的存储。

对于分区之后,在先将管理的数据写入块中,即格式化。因此在使用一个盘时,我们都是要先分区,在格式化。例如我们电脑的c盘,d盘就是分区,并且我们可以选择格式化,格式化的本质是清除我们自己的文件数据,对于文件管理数据是不会修改的。

inode

在Linux系统中,我们在使用ls指令时,还有一个选项:

ls -li  //可查看文件的inode

一般情况,一个文件对应一个inode,基本上文件都有inode,inode是一个数字,在整个分区中具有唯一性,因此在Linux内核中,识别文件与文件名无关,而是依据inode。 

为了能解释清楚 inode 我们先简单了解一下文件系统:

也就是上图,可以看到我们写入管理信息到块中有许多,我们先来看看inode table。

inode_table

一般叫做i节点表,inode table主要存放文件属性,所有者,最近修改时间等。i节点表里面也是存放了许多inode,inode大小为128字节,我们将它理解为一个数组,他的下标就是表示inode位置。

每一个分组(table)都有一个起始的start_inode与记录。在分区的唯一性就是自身位置加上start_inode来确定。

data_blocks

数据区,没有任何管理数据,只是一个以4KB的大小的数据块区域,那么一个块里面能存放这么多文件,如何确定文件对应的数据块?   其实在inode中,为我们维护了一个数组 blocks[ ],记录了位置。

inode bitmap

利用位图来记录这么多文件的使用情况,0表示未使用,1表示使用。

block bitmap

除了文件的使用情况,我们还需要记录数据块的使用情况,因此利用位图记录使用情况。

那么了解了这些我们大概能知道创建和删除文件本质上就是去修改inode位图,数据块位图里的某个位置,0变1,1变为0。所以并未真正的删除数据。

Block Group 文件系统会根据分区的大小划分为数个 Block Group 。而每个 Block Group 都有着相 的结构组成。
超级块( Super Block ):存放文件系统本身的结构信息。记录的信息主要有: bolck inode 的总量, 未使用的block inode 的数量,一个 block inode 的大小,最近一次挂载的时间,最近一次写入数据的 时间,最近一次检验磁盘的时间等其他文件系统的相关信息。Super Block 的信息被破坏,可以说整个 文件系统结构就被破坏了。
GDT Group Descriptor Table :块组描述符,描述块组属性信息
创建一个新文件主要有一下 4 个操作:
1. 存储属性
内核先找到一个空闲的 i 节点( 。内核把文件信息记录到其中。
2. 存储数据
该文件需要存储在三个磁盘块,内核找到了三个空闲块 。将内核缓冲区的第一块数据
复制到第一块中 ,下一块复制到下一个 ,以此类推。
3. 记录分配情况
文件内容按顺序 存放。内核在 inode 上的磁盘分布区记录了上述块列表。
4. 添加文件名到目录

了解了这些,可是我们一般在Linux中也不用inode啊,我们都是用文件名,这是怎么弄得?

因为用户只认识文件名,操作系统只认识Inode,因此我们需要将文件名与inode一一映射,

一一映射急就是键值对的关系,我们将文件名做为key值,将Inode作为valu,以这样方式一一映射。

因此文件名不属于文件属性。那么如何查找一个文件?其实就是先获取到文件的inode,再根据inode确定分区位置,确定inode编号,确定inode_table,但是我们是一层层往下找的,先从根目录开始,找到下一个目录,依次往下,知道我们需要的文件的位置。

这样找太慢了,于是进程会将当前的目录也会保存到cache中。

挂载

一个磁盘想要被使用,就需要将他分区,而想要使用这个分区,还需要将每一个分区都需要挂载到相应的目录上,挂载其实就是将目录的数据结构与文件的数据结构联系起来。

软硬链接

建立软链接:

ln -s file.c file.c.soft.link  //给log文件建立软链接

此时ls -li可以看到inode变化,对应的

建立硬链接:

ln test.c test.c.hard.link //后者硬链接前者

再查看此时的inode没发生变化,但对应的inode编号数字增加了。链接数也发生了变化。

因此我们可以得出,软链接是一个独立的文件,硬链接不是一个独立的文件。因为没有独立的inode编号.

那么什么是软硬链接?

首先我们肯定队快捷方式很熟悉,我们的电脑是双击图标就可以把程序加载到内存中,开始运行这个程序,但事实上我们都是到我们在运行程序时都是运行它的exe文件,一般这个文件在目录下很深的地方,用户每次想加载该文件都需要找到他太国麻烦,因此就可以用一个链接文件当作exe文件,把它拿出来当作我们的快捷方式来加载程序。

而链接分了两种方式:

1.软链接就相当一个wndows的快捷方式,就是一个普通文件,可以快速定位文件。

2.硬链接不是一个独立的文件,从inode往后和目标文件是一个东西,本质上就是在指定目录内部的一组映射关系。

普通文件链接后链接数为1,而空目录连接后,默认链接数位2,这是因为他有两个隐藏文件:

.当前目录  ..上一级目录,他们都记录着对应的inode。

用户无法对目录建立硬链接,对于隐藏文件 .. 和.是系统创建的硬链接,但不允许用户来创建。 

二,动静态库

什么是动静态库?

动态库(.a):程序在编译链接的时候,把库里的代码链接到可执行程序当中,运行的时候不再需要库文件。

静态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码

注意:

1.一个与动态库链接的文件,仅仅包含它会使用到的函数接口地址的一个表,而不是函数所在文件的整个机器码

2.在程序运行之前,外部函数的机器码由操作系统从磁盘的动态库复制到内存当中,这个过程称为动态链接。

3.动态库可以在多个文件间共享,因此动态链接使得可执行文件更小,可以节省磁盘的空间,操作系统利用虚拟地址机制,使得物理空间对应的一份动态库可以被所有进程共享,节省了磁盘空间与内存。

为了更好的了解动静态库,我们分三个角度看待动静态库:

1.站在库的制作者角度

静态库:

制作一个静态库

首先库中是没有主函数的,比如我们现在搞一个加减乘除的一个库,先创建对应的头文件与.c文件

创建并完成这些文件的代码编写,之后再创建一个maintest.c,用于测试。

完成书写后,将所有文件编译成一个可执行:

gcc -o TEST Add.c Sub.c Mul.c Dic.c Maintest.c

我们运行TEST,可以看到没什么问题,

我们以这种方式编译成可执行文件,但是这种放式并不是最佳方式,我们一般将所有.c文件都先编译成.o文件,再将我们需要的.o文件链接在一起。

gcc -c 源文件 -o目标.o文件
//一般我们直接使用gcc -c源文件 会自动生成同名的.o文件

编写makefile以这样的方式去编译可执行: 

TEST:Add.o Sub.o Mul.o Div.o Maintest.ogcc -o $@ $^
%.o:%.cgcc -c $<
#$<代表一个个的去取文件列表的文件.PHONY:clean
clean:rm -f *.o TEST

于是我们为了其他人想要使用对应的.o文件,我们将所有的.o文件与头文件都打包在一起,用的时候直接链接某一个你需要的.o文件。

故一般在编译文件时,我们只编译成.o文件即可,比如这里我们就将所由.o文件与头文件放在一个test目录下。该目录就当作是用户的目录,此时有一个Maintest.c是你的主程序,我们想将她编译成.o文件,之后想要链接哪一个.o文件,就一起gcc .o文件即可。

这里再将所有的.o文件打个包就是我们所说的库。、

打包.o文件

这里用ar(gnu归档工具进行打包)指令打包:

ar -rc .o文件 -o 目标文件 

 如打包我们这里的.o文件,我们在makefile中打包:

#这里我们用一个变量表示库的名字
static-lib=libmymath.a
$(static-lib):Add.o Sub.o Mul.o Div.oar rc $@ $^%.o:%.cgcc -c $<
.PHONY:clean
clean:rm -f *.o *.a

之后创建的libmymath.a就是我们的静态库。

2.站在静态库使用者的角度

有了静态库,那么我们现在要去使用它,首先将头文件在拿到用户需要编译的目录下,头文件一般都是开放的,之后直接编译,我们发现它会报链接的错误,有了静态库和头文件还不行,我们还需要链接,我们自己写的第三方库gcc是不认识的,我们需要指定连接:

gcc 目标.c文件 -l 静态库  //链接静态库到目标文件中
这里哭的名字一般是要去掉lib与.a才是真实名字如果还是不认识
gcc 目标.c文件 -l 名字 -L .  //当前下的该库

链接完成之后就会形成一个a.out的可执行文件。

此时对于gcc来说c标准库可以直接用,不需要指明链接,也就是第二方库。

gcc默认是动态链接的,有序使用动态库连接,如果子提供了静态库,还是会把库链接起来。

但是这又是是静态库,又是头文件,太挫了,所以最终呈现给用户的形式是将库文件与头文件分开的两个目录,并且在一个目录下,我们在makefile中进行分隔:

static-lib=libmymath.a$(static-lib):Add.o Div.o Mul.o Sub.oar -rc $@ $^
%.o:%.cgcc -c $<.PHONY:output
output:mkdir -p mymath_lib/includemkdir -p mymath_lib/libcp -f *.h mymath_lib/includecp -f *.a mymath_lib/lib.PHONY:clean
clean:rm -rf *.o *.a mymath_lib

之后我们还可以将该目录压缩,其他用户需要用的是后给他压缩包,解压之后就有了该目录,使用的时候,使用指令:

gcc Maintest.c -I mymath-lib/include

然后再链接库即可:

ac ar Maintest.c -l mymath -L mymath-lib/lib

 当然我们可以一起使用:

gcc Maintest.c -I mymath-lib/include -l mymath -L mymath-lib/lib

动态库

作为制作者

动态库与静态库在这里的区别就是打包方式的不同:

首先打包时使用的指令为

gcc -shared -o 目标文件 源文件

之后编译时我们还需要添加选想 -fPIC

gcc -fPIC -c .c文件

动态库生成的Makefile为:

dy-lib=libmymath.so$(dy-lib):Add.o Div.o Mul.o Sub.ogcc -shared -o  $@  $^   #打包围为动态库
%.o:%.cgcc -fPIC -c $<    #增加一个选项生成位置无关码.PHONY:output
output:mkdir -p mymath_lib/includemkdir -p mymath_lib/libcp -f *.h mymath_lib/includecp -f *.so mymath_lib/lib.PHONY:clean
clean:rm -rf *.o *.so  mymath_lib

作为使用者

与静态库不同,动态库生成之后就没有链接错误了,不过有编译错误,会报错头文件找不到,因此只需要与静态库链接一样的指令,在编译时指定链接(link)我们的动态库:

 gcc Maintest.c -I mymath_lib/include/ -l mymath -L mymath_lib/lib/

生成了可执行文件还是要依靠动态库,否则无法运行,而之前,我们指定动态库是编译器知道的,一旦生成完,可执行与动态库还是相互不认识,ldd查看动态库就会发现,找不到。这里有四种方法:

1.下载到我们的系统库里。直接将库拷贝到lib64里,之后gcc -l 指定的库,就可以成功运行了。

2.将我们的库与该路径下的一个文件建立软链接。在将该软链接移动到库上。这样只要有谁想找到这个库,我们就会迅速定位到该库。

当然我们建立软连接时最好给这个库的绝对路径,例如我这个:

3.在Linux中,还存在一个加载项LD_LIBRARY_PATH,加载库路径的环境变量。因此我们可以把动态库的路径找出来,然后导入环境变量。

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home//danchengwei/myfile/file2/test/mymath_lib/lib/libmymath.so

4.直接更改系统配置文件

在系统目录下游体格etc/ld.so.conf.d/的配置文件目录,在目录里我们会创建一个文件里面用来存放你要找的动态库的路径。

如果我们的库既有动态也有静态库,默认使用动态库。

当然我们就可以使用别人的库,

ncurses库

下载之后就可以在lib64下找到它的动态库文件。

使用外部库
系统中其实有很多库,它们通常由一组互相关联的用来完成某项常见工作的函数构成。比如用来处理屏幕显示情况 的函数(ncurses 库)
gcc -Wall calc.c -o calc -lm
-lm 表示要链接 libm.so 或者 libm.a 库文件

 动态库加载

将方法的地址与可执行程序链接起来,因此动态库连接的程序不仅仅程序要加载,动态库也要加载,态库是一种共享库,它可以在程序运行时加载。在Linux中,您可以使用dlopen函数打开动态链接库,使用dlsym函数获取函数执行地址,使用dlclose函数关闭动态链接库。这些函数都在dlfcn.h头文件中声明.

 在编译的时候,代码就有了虚拟地址,用基地址+偏移量表示,并一一映射,这种模式称为平坦模式。

 

这篇关于Linux学习之文件系统与动静态库的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/655781

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

linux-基础知识3

打包和压缩 zip 安装zip软件包 yum -y install zip unzip 压缩打包命令: zip -q -r -d -u 压缩包文件名 目录和文件名列表 -q:不显示命令执行过程-r:递归处理,打包各级子目录和文件-u:把文件增加/替换到压缩包中-d:从压缩包中删除指定的文件 解压:unzip 压缩包名 打包文件 把压缩包从服务器下载到本地 把压缩包上传到服务器(zip

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

Linux 网络编程 --- 应用层

一、自定义协议和序列化反序列化 代码: 序列化反序列化实现网络版本计算器 二、HTTP协议 1、谈两个简单的预备知识 https://www.baidu.com/ --- 域名 --- 域名解析 --- IP地址 http的端口号为80端口,https的端口号为443 url为统一资源定位符。CSDNhttps://mp.csdn.net/mp_blog/creation/editor

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss