[Linux]理解文件系统!动静态库详细制作使用!(缓冲区、inode、软硬链接、动静态库)

        hello,大家好,这里是bang___bang_,今天来谈谈的文件系统知识,包含有缓冲区、inode、软硬链接、动静态库。本篇旨在分享记录知识,如有需要,希望能有所帮助。

目录

1️⃣缓冲区

🍙缓冲区的意义

🍙常见缓冲区刷新策略

🍙缓冲区位置猜想

🍥现象猜测

🍥现象解释

🍙用户级缓冲区位置

2️⃣理解文件系统

🍙磁盘的存储结构

🍥磁盘物理结构

🍥磁盘抽象结构

🍙文件系统

🍥inode vs 文件名

3️⃣软硬链接

🍙软链接

🍙硬链接

4️⃣动态库和静态库

🍙静态库

🍥生成静态库

🍥使用静态库

🍙动态库

🍥生成动态库

🍥使用动态库

🍙同时存在使用静态库还是动态库?

🍙特点总结

🍥静态库特点

🍥动态库特点


1️⃣缓冲区

问题:什么是缓冲区?

答:就是一段内存空间!!

🍙缓冲区的意义

我们知道了一段内存空间就是缓冲区,那么为什么要有缓冲区呢?

🌰生活例子映射:

        你在西安,你有个好朋友在上海,下个月好朋友要过生日了,你想送他一本你自己手绘的图画,你可以选择自己骑车亲手送给你的朋友;也可以选择下楼到顺丰选择寄送包裹然后回家。

毫无疑问:你自己骑车亲手送需要花费大量的时间,而你选择去顺丰寄包裹却很快,但是寄包裹也不是立马就会发送包裹,可能要等仓库堆满一批货物再一起发送。(图画是数据,顺丰是缓冲区)

自己骑车亲手送就相当于写透模式(WT)

而去顺丰寄包裹再可以直接回家就相当于写回模式(WB)

写透模式:直接将数据写到外部设备。

写回模式:先将数据写到缓冲区,当缓冲区的数据达到一定量时,再集中写到外部设备。

通过这个例子很显然能感受到缓冲区存在的意义了!

缓冲区存的意义:提高整机效率。主要是为了提高用户的响应速度!

🍙常见缓冲区刷新策略

缓冲策略=一般+特殊 

一般情况: 

        立即刷新

        行刷新(行缓冲)

        满刷新(全缓冲)

特殊情况:

        用户强制刷新(fflush)   

        进程退出   

一般而言:行缓冲的设备文件——显示器              全缓冲的设备文件——磁盘文件

        所有的设备,永远都倾向于全缓冲!

——缓冲区满了,才刷新—>更少次的IO操作—>更少次的外设的访问(提高效率)    

        和外部设备IO的时候,数据量的大小不是主要矛盾,和外设预备IO的过程是最耗费时间的!!

🍙缓冲区位置猜想

🍥现象猜测

🌰下面有一段代码,我们分别输出到显示屏,输出重定向到文件。

int main() {//C语言提供的printf("hello printf\n");fprintf(stdout, "hello fprintf\n");const char* s = "hello fputs\n";fputs(s, stdout);//OS提供的const char* ss = "hello write\n";write(1, ss, strlen(ss));fork();return 0;
}
现象图

        我们可以看到同一份代码,输出的结果却不一样,C的IO接口打印了2次,系统接口只打印1次和向显示器打印一样!也就是说子进程中有父进程C的IO接口对应的打印数据,但没有系统接口的。也就是说如果有缓冲区,那么绝对是C标准库来提供的

🍥现象解释

        ★如果向显示器打印,刷新策略是行刷新,那么进程执行到fork()函数时会将C标准库里缓冲区的数据全部进行刷新出去(fork无意义!)

        对于进程来说,当我们调用C文件接口fputs时,实际是将进程数据写入到C标准库中的缓冲区里,然后再统一调用系统接口write函数写入到对应的目标文件中。 

        如果我们进行输出重定向时,将原本写入到stdout文件中的数据写入到了磁盘文件中,缓冲模式就由行刷新变成了全缓冲。(\n便没有意义了!)当进程执行到代码fork()时,此时进程写入C标准库中的缓冲区数据还未刷新。当进程执行fork函数,便又生成了子进程。

        fork后父子进程退出:刷新数据到磁盘文件中,但是刷新实际上也是一次写入,因为进程的独立性,发生写时拷贝,打印2份!!

        ★缓冲区里的数据也是父进程的数据!提前强制刷新后,没有数据了,子进程也就没拷贝了!

🍙用户级缓冲区位置

问题:为什么fflush只传了stdout,却能找到缓冲区?

答:C语言中,打开文件,FILE* fopen(),struct FILE 结构体 内部封装了fd,还包含了该文件fd对应的语言层的缓冲区结构!

2️⃣理解文件系统

🌰我们使用ls -l指令读取文件信息,实际上是对磁盘中的文件进行读取。

磁盘——永久性存储介质(还有SSD,U盘,flash卡,光盘,磁带) 

磁盘是一个外设+还是我们计算机中唯一的一个机械设备!也就是说速度很慢!(相对于CPU)

🍙磁盘的存储结构

🍥磁盘物理结构

磁盘盘片,磁头,伺服系统,音圈马达等等

向磁盘写入,本质就是改变磁盘上的正负性。

磁盘的盘面被划分为一个个磁道,而磁道又被划分为一个个扇区。

扇区(磁道划分区域)是磁盘存储数据的基本单位(512byte)

如何将数据写入指定的一个扇区?有以下步骤:CHS寻址

    ——1.在哪一个面上(对应的就是哪一个磁头)

    ——2.在哪一个磁道(柱面)上

    ——3.在哪一个扇区上

如果我们有了CHS寻址方式,就可以找到任意一个扇区。

🍥磁盘抽象结构

小时候我们都有过磁带这种东西,他是缠在一起成圈的,但是我们也可以将他全拉出来成线状。磁盘我们也可以抽象成拉长后变为线状结构。

结构:圆形结构(CHS)->线性结构(LBA)

LBA是非常单纯的一种寻址模式﹔从0开始编号来定位扇区,第一扇区LBA=0,第二扇区LBA=1,依此类推。所以将来我们想要访问磁盘的某个扇区,只需要将通过LBA寻址后转换为CHS物理寻址。

最终:对磁盘的管理就变成了对一个个小分区的管理。 

🍙文件系统

磁盘文件系统图
上图为磁盘文件系统图(内核内存映像肯定有所不同), 磁盘是典型的块设备 ,硬盘分区被
划分为一个个的 block 。一个 block 的大小是由格式化的时候确定的,并且不可以更改。

虽然磁盘的基本单位是扇区(512字节),但是OS(文件系统)和磁盘进行IO的基本单位是:4KB(8*512byte)4KB->block大小

        ★Super Block->文件系统的属性信息

        ★Data bolcks->多个4KB大小的集合,保存的都是特定文件的内容

        ★inode Table:inode是一个大小为128字节的空间,保存的是对应文件的属性该块组内,所有文件的inode空间的集合,需要标识唯一性,每一个inode块,都要有一个inode编号!

        ★Block Bitmap:假设有10000+blocks,10000+比特位;比特位合特定的block是一一对应的,其中比特位为1,代表该block被占用,否则表示可用!

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

        ★GDT:块组描述符,这个块组多大,已经使用了多少了,有多少个inode,已经占用了多少个,还剩多少,一共有多少个block,使用了多少....

我们将块组分割成上面的内容,并且写入相关的管理数据->每一个块组都这么干->整个分区就被写入了文件系统信息!!!(格式化)

🍥inode vs 文件名

  • 一个目录下,可以保存很多文件,但是这些文件都不会重名。
  • 目录是文件,目录有自己的inode和Data block

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

🌰为什么目录需要w权限?

因为在目录下创建文件时,这个目录有自己的数据块,我们创建文件的文件名就在目录的Data block 中,所以我们要将文件名和inode编号写入保存,此时必须需要w权限。

🌰为什么目录中具有r权限?

当我们需要显示文件名时,我们只能从目录的内容中获取文件名及相关属性,就必须访问目录的文件内容,就必须需要r权限从目录的Data block中获取文件名。 

🌰创建文件,系统做了什么?

特定分组找到没有使用的inode,分配inode编号,如果文件有内容,向文件内容当中申请Data Block,设置Block Bitmap,建立inode和Bitmap的映射,inode和Bitmap、Data Block的对应关系并写到inode结点中,inode文件名对应的映射关系写到特定的目录的DataBlock中。

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

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

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

根据文件名找到inode,然后查内容查属性。

3️⃣软硬链接

本质区别:有没有独立的inode

🍙软链接

软链接有独立的inode,软链接是一个独立的文件

应用:相当于windows下的快捷方式

特性:可以理解成为:软链接的文件内容,是指向的文件对应的路径!

ln -s 文件 软链接文件名

🌰建立一个软链接

🌰 软链接如同Windows下的快捷方式

🍙硬链接

硬链接没有独立的inode,硬链接不是一个独立的文件(有被链接文件的inode)

创建硬链接就是在指定的目录下,建立了 文件名 和 指定inode的映射关系。

ln 文件 硬链接文件名

🌰建立一个硬链接

硬链接没有独立的inode!也就是说硬链接不是一个独立的文件!

🌰硬链接数(引用计数)

硬链接后inode与文件名映射关系增加1组,所以为2,从这里可以看出一个思想:

当我们删除一个文件的时候,并不是把这个文件inode删除,而是将这个文件的inode引用计数--。当引用计数为0的时候,这个文件,才正在删除!!(RAII思想)

🌰默认创建文件引用计数是1,创建目录引用计数是2

inode与文件名对应一组映射关系。

但目录为什么是2呢?

因为目录里面还有隐藏文件.文件,也就是说inode对应2个文件名(自己目录名,自己目录内部的文件名),所以引用计数为2。

4️⃣动态库和静态库

🍙静态库

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

🍥生成静态库

打包.o文件:ar -rc xxx.a xxx.o xxx.o
解析 ar是gnu归档工具 ar——archieve  r——replace c ——create ,把.o文件打包到.a(静态库)

🌰将myprint.o、mymath.o打包到libtest.a

myprint.h代码:

#pragma once#include<stdio.h>
#include<time.h>extern void Print(const char* str);

myprint.c代码:

#include"myprint.h"void Print(const char* str)
{printf("%s[%d]\n",str,(int)time(NULL));
}

mymath.h代码:
 

#pragma once#include<stdio.h>extern int addToTarget(int form,int to);

mymath.c代码:

#include"mymath.h"int addToTarget(int form,int to)
{int sum=0;for(int i=form;i<=to;i++){sum+=i;}return sum;
}

Makefile:

libtest.a:mymath.o myprint.oar -rc libtest.a mymath.o myprint.o
mymath.o:gcc -c mymath.c -o mymath.o -std=c99
myprint.o:gcc -c myprint.c -o myprint.o -std=c99.PHONY:clean
clean:rm -rf *.o *.a 
静态库生成图

🍥使用静态库

gcc main.c -I 指定头文件搜索路径 -L 指定库文件搜索路径 -l使用哪个库

🌰使用上面生成的libtest.a静态库

修改Makefile文件,将头文件放到include目录中,静态库放到lib目录中。

libtest.a:mymath.o myprint.oar -rc libtest.a mymath.o myprint.o
mymath.o:gcc -c mymath.c -o mymath.o -std=c99
myprint.o:gcc -c myprint.c -o myprint.o -std=c99.PHONY:output
output:mkdir -p libmkdir -p includecp -rf *.h includecp -rf *.a lib.PHONY:clean
clean:rm -rf *.o *.a lib include

使用静态库生成可执行文件:

🍙动态库

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

🍥生成动态库

生成动态库的必须加 -fPIC 生成二进制文件

-shared告诉gcc生成动态库

gcc -fPIC -c xxxx.c -o xxxx.o //生成动态库必须加-fPIC
gcc -shared xxxx.o -o libxxxx.so  //-shared告诉gcc生成动态库

🌰生成libtest.so动态库

编写Makefile文件:

libtest.so:mymath_d.o myprint_d.ogcc -shared mymath_d.o myprint_d.o -o libtest.so
mymath_d.o:mymath.cgcc -fPIC -c mymath.c -o mymath_d.o -std=c99
myprint_d.o:myprint.cgcc -fPIC -c myprint.c -o myprint_d.o -std=c99.PHONY:output
output:mkdir -p libmkdir -p includecp -rf *.h includecp -rf *.so lib.PHONY:clean
clean:rm -rf *.o  *.so lib include
生成动态库

🍥使用动态库

动态库的使用和静态库是一样的。

gcc main.c -I 指定头文件搜索路径 -L 指定库文件搜索路径 -l使用哪个库

🌰main.c使用动态库libtest.so

查看程序链接的库(动态库):ldd

ldd 可执行程序    //查看程序链接的库

🍙同时存在使用静态库还是动态库?

问题:假设现在既有静态库又有动态库,那么程序默认链接的是哪种库?

修改Makefile文件:

.PHONY:all
all:libtest.so libtest.alibtest.so:mymath_d.o myprint_d.ogcc -shared mymath_d.o myprint_d.o -o libtest.so
mymath_d.o:mymath.cgcc -fPIC -c mymath.c -o mymath_d.o -std=c99
myprint_d.o:myprint.cgcc -fPIC -c myprint.c -o myprint_d.o -std=c99libtest.a:mymath.o myprint.oar -rc libtest.a mymath.o myprint.o
mymath.o:gcc -c mymath.c -o mymath.o -std=c99
myprint.o:gcc -c myprint.c -o myprint.o -std=c99.PHONY:clean
clean:rm -rf *.o *.a *.so 

现象:

验证发现同时存在静态库和动态库默认使用的是动态库

那么如何在这种情况使用静态库呢?  -static  指定静态链接

-static的意义:摒弃默认优先使用动态库的原则,而是直接使用静态库。

使用ldd查看链接的动态库,报错显示:不是动态可执行文件,也就是说使用的是静态库!!

🍙特点总结

🍥静态库特点

                优点:

                ①静态库被打包到应用程序中加载速度快
                ②发布程序无需提供静态库,移植方便

                缺点:

                ①相同的库文件数据可能在内存中被加载多份,消耗系统资源,浪费内存
                ②库文件更新需要重新编译项目文件,生成新的可执行程序,浪费时间

🍥动态库特点

                优点:

                ①可实现不同进程间的资源共享
                ②动态库升级简单,只需要替换库文件,无需重新编译应用程序
                ③可以控制何时加载动态库,不调用库函数动态库不会被加载                                 

                缺点:

                ①加载速度比静态库慢
                ②发布程序需要提供依赖的动态库


        文末结语,开篇解释缓冲区以及意义,并验证了用户级缓冲区的刷新策略,接下来谈文件系统,首先介绍磁盘的存储结构(包括物理结构和抽象结构),介绍inode和文件名之间的关系,软硬链接的使用,最后讲解动静态库,详细说明如何制作并使用动静态库,并探究了动静态库同时存在时默认使用动态库,以及想使用静态库的解决方案,最终总结动静态的特点。本篇旨在分享记录知识,如有需要,希望能有所帮助!!感谢观看!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/24114.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python(六十九)为什么要将元组设计成不可变序列

❤️ 专栏简介&#xff1a;本专栏记录了我个人从零开始学习Python编程的过程。在这个专栏中&#xff0c;我将分享我在学习Python的过程中的学习笔记、学习路线以及各个知识点。 ☀️ 专栏适用人群 &#xff1a;本专栏适用于希望学习Python编程的初学者和有一定编程基础的人。无…

nginx服务

目录 基本介绍 nginx的主要功能 nginx的主要应用场景 nginx常用命令 nginx另外一种安装方式 nginx常用的信号符&#xff1a; nginx配置文件详解 全局配置 event模块 http模块 server模块 location模块&#xff1a; 模块的划分 基本介绍 nginx&#xff1a;高性能、…

06 Ubuntu22.04上的miniconda3安装、深度学习常用环境配置

下载脚本 我依然是在清华镜像当中寻找的脚本。这里找脚本真的十分方便&#xff0c;我十分推荐。 wget https://mirrors.tuna.tsinghua.edu.cn/anaconda/miniconda/Miniconda3-latest-Linux-x86_64.sh 下载十分快速&#xff0c;10秒解决问题 运行miniconda3安装脚本 赋予执…

python数据容器

目录 数据容器 反向索引 list列表 语法 案例 列表的特点 列表的下表索引 list的常用操作 list列表的遍历 while循环遍历 for循环遍历 tuple元组 前言 元组定义 元组特点 获取元组元素 元组的相关操作 元组的遍历 while循环遍历 for循环遍历 字符串 前言…

LeetCode113. 路径总和 II

113. 路径总和 II 文章目录 [113. 路径总和 II](https://leetcode.cn/problems/path-sum-ii/)一、题目二、题解方法一&#xff1a;递归另一种递归版本方法二&#xff1a;迭代 一、题目 给你二叉树的根节点 root 和一个整数目标和 targetSum &#xff0c;找出所有 从根节点到叶…

java+python企业会议在线办公微信小程序 ia505

一、小程序端功能 该部分内容提供员工注册、员工资料修改、通知公告、部门信息、会议记录等等功能。 二、管理员管理功能 该部分内容包含了首页、个人中心、通知公告管理、员工管理、部门信息管理、职位信息管理、会议记录管理、待办事项管理、工资信息管理、留言板管理、系统管…

C++ 类型兼容规则

类型兼容规则是指在需要基类对象的任何地方&#xff0c;都可以使用公有派生类的对象来替代。 通过公有继承&#xff0c;派生类得到了基类中除构造函数和析构函数之外的所有成员。这样&#xff0c;公有派生类实际就具备了基类的所有功能&#xff0c;凡是基类能解决的问题&#x…

微信支付官方文档怎么看

博主介绍&#xff1a;✌全网粉丝3W&#xff0c;全栈开发工程师&#xff0c;从事多年软件开发&#xff0c;在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战&#xff0c;博主也曾写过优秀论文&#xff0c;查重率极低&#xff0c;在这方面有丰富的经验…

Cilium系列-14-Cilium NetworkPolicy 简介

系列文章 Cilium 系列文章 前言 今天我们进入 Cilium 安全相关主题, 介绍 Kubernetes 网络策略以及 CiliumNetworkPolicies 额外支持的内容。 网络策略(NetworkPolicy)的类型 默认情况下&#xff0c;Kubernetes 集群中的所有 pod 都可被其他 pod 和网络端点访问。 网络策…

Kafka3.0.0版本——Broker(Zookeeper服务端存储的Kafka相关信息)

目录 一、启动zookeeper集群及kafka集群服务启动1.1、先启动三台zookeeper集群服务&#xff0c;再启动三台kafka集群服务1.2、使用PrettyZoo连接zookeeper客户端工具 二、在zookeeper服务端存储的Kafka相关信息 一、启动zookeeper集群及kafka集群服务启动 1.1、先启动三台zook…

计算机成下一个土木了吗?

前些年抓住了互联网行业的红利期&#xff0c;进入大厂的员工&#xff0c;基本可以实现在一线城市买房扎根。 但反观现在&#xff0c;“被毕业、逃离互联网、躺平算了...”却成了这个行业的主旋律&#xff0c;不少人在谈论润到国企和外企去了&#xff0c;也放低了对工资的预期&…

LIME(可解释性分析方法)

目录 1.什么是LIME 2.思路 3.LIME在不同任务中的范式&#xff08;待补充&#xff09; 1.什么是LIME 简单理解&#xff1a; 对于分类任务&#xff1a;如下图所示&#xff0c;LIME可以列出分类结果&#xff0c;所依据特征对应给比重。 对于图像分类任务&#xff1a;如下图所示&a…

docker 配置 Mysql主从集群

Docker version 20.10.17, build 100c701 MySQL Image version: 8.0.32 Docker container mysql-master is source. mys ql-replica is replication. master source. replica slave.名称叫法不一样而已。 Choose one of the way&#xff0c;与replica同步数据两种情况&…

【Shell】基础语法(一)

文章目录 一、shell的介绍二、执行脚本三、shell的基本语法1. 变量的使用2. 变量的分类 一、shell的介绍 Shell的作用是解释执行用户的命令&#xff0c;用户输入一条命令&#xff0c;Shell就解释执行一条&#xff0c;这种方式称为交互式&#xff08;Interactive&#xff09;&a…

不规则文件转JSON

需求分析&#xff1a; 有时候&#xff0c;我们取出来的数据并不是一个规则的JSON文件&#xff0c;这个时候面对存库还是ES检索都是一个问题&#xff0c;所以我们就需要进行解析&#xff0c;然而用字符串分割是不现实的&#xff0c;我们需要一种快速的方法。 问题解决&#x…

Linux-centos花生壳实现内网穿透

Linux-centos花生壳实现内网穿透 官网教程 1.安装花生壳 下载网址 点击复制就可以复制下载命令了 wget "https://dl.oray.com/hsk/linux/phddns_5.2.0_amd64.rpm" -O phddns_5.2.0_amd64.rpm# 下载完成之后会多一个rpm文件 [rootlocalhost HuaSheng]# ls phddns_…

flask中的应用上下文

flask中的应用上下文 Flask应用上下文主要包含两个对象&#xff1a;current_app和g。这两个对象在处理请求期间都是全局可访问的&#xff0c;但在每个请求结束时都会被重置。 current_app&#xff1a;这是当前激活的Flask应用的实例。在大多数情况下&#xff0c;你可以将其视为…

刷题DAY16

题目一 给定两个字符串str1和str2&#xff0c;再给定三个整数ic、dc和rc&#xff0c;分别代表插入、删除和替换一个字符的代价&#xff0c;返回将str1编辑成str2的最小代价。【举例]str1"abc",str2“adc",ic5,dc3,rc2从“abc“编辑成adc",把b替换成d是代价…

zookeeper --- 高级篇

一、zookeeper 事件监听机制 1.1、watcher概念 zookeeper提供了数据的发布/订阅功能&#xff0c;多个订阅者可同时监听某一特定主题对象&#xff0c;当该主题对象的自身状态发生变化时(例如节点内容改变、节点下的子节点列表改变等)&#xff0c;会实时、主动通知所有订阅者 …

epoll、poll、select的原理和区别

select&#xff0c;poll&#xff0c;epoll都是IO多路复用的机制。I/O多路复用就是通过一种机制&#xff0c;一个进程可以监视多个描述符&#xff0c;一旦某个描述符就绪&#xff08;一般是读就绪或者写就绪&#xff09;&#xff0c;能够通知程序进行相应的读写操作。但select&a…