[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循环遍历 字符串 前言…

SpringBoot复习:(19)Condition接口和@Conditional注解

Condition接口代码如下&#xff1a; public interface Condition {boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);}它是一个函数式接口&#xff0c;只有一个方法matches用来表示条件是否满足。matches方法中的ConditionContext类对象context可以…

OPENCV C++(五)滤波函数+sobel边缘检测+人脸磨皮mask

滤波函数 中值滤波 medianBlur(frame, detectmat, 5); 平均滤波 blur(frame, detectmat, Size(5, 5)); 高斯滤波&#xff08;最后一个是方差 越大越模糊&#xff09; GaussianBlur(frame, detectmat, Size(5, 5),0); sobel的边缘检测函数 Sobel(gray, dx, CV_16S, 1, 0, 3…

图片静态展示

图片静态展示程序&#xff0c;包含选择文件夹路径&#xff0c;旋转&#xff0c;放大缩小&#xff0c;拖动&#xff0c;幻灯片播放&#xff0c;上一张下一张等&#xff0c;程序使用QT实现。 程序下载地址&#xff1a; 图片静态展示&#xff0c;包含选择文件夹路径&#xff0c;…

LeetCode113. 路径总和 II

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

Golang之路---04 项目管理——编码规范

本文根据个人编码习惯以及网络上的一些文章&#xff0c;整理了一些大家能用上的编码规范&#xff0c;可能是一些主流方案&#xff0c;但不代表官方。 1. 文件命名 由于 Windows平台文件名不区分大小写&#xff0c;所以文件名应一律使用小写 不同单词之间用下划线分词&#xf…

第20周 服务容错-Hystrix

RabbitMQ 安装 1. 首先在Linux上进行一些软件的准备工作&#xff0c;yum下来一些基础的软件包 yum install build-essential openssl openssl-devel unixODBC unixODBC-devel make gcc gcc-c kernel-devel 2. 下载RabbitMQ所需软件包&#xff08;本神在这里使用的是 RabbitM…

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

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

二叉树的直径 LeetCode热题100

题目 给你一棵二叉树的根节点&#xff0c;返回该树的 直径 。 二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根节点 root 。 两节点之间路径的 长度 由它们之间边数表示。 思路 分析二叉树的直径&#xff0c;无非是一个节点加上…

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…