Linux篇:文件系统
一、共识原理:
文件=文件内容+文件属性
磁盘上存储文件=存文件的内容(数据块)+存文件的属性(inode)
Linux的文件在磁盘中存储是将属性和内容分开存储的。
二、硬件简述:
1. 认识硬件
磁盘:唯一的一个机械设备,也是一个外设。磁头一面一个,磁头和盘面不接触。是永久性存储介质,高温会退磁。
内存:掉电易失性存储介质。
磁带:类似于磁盘。
2、磁盘的存储构成
磁盘被访问的最基本单元是扇区(512字节/4KB),我们可以把磁盘看作有无数个扇区构成的存储介质。
要把数据存到磁盘,第一个解决的问题是定位一个扇区,哪一面定位用哪个磁头,哪一个磁道,哪一个扇区。
磁头左右摆动,其实质是定位磁道和柱面的过程(运动越少,效率越高;运动越多,效率越低)。
在软件设计上,设计者一定要有意识的将相关数据放在一起。
磁带延展开,逻辑上是线性的,所以磁盘在逻辑上也是线性的。基于扇区的数组,任意一个扇区都有下标。
3、回归到硬件:不仅仅有CPU有寄存器,其他设备外设也有,磁盘也有。
控制寄存器(rw):控制IO方向。
数据寄存器:存储数据。
地址寄存器:逻辑地址(LBA)。
状态寄存器:表示结果。
三、对硬件进行抽象理解(文件系统ext2):
1、分治: 分区->块组->文件系统。
Data blocks:存文件内容的区域,以块的形式呈现,常见的文件系统块大小是4KB(一般而言,每个块只有自己的数据)。
inode Table:很多inode。
inode:inode结构体,包含单个文件的所有属性(inode number,文件类型,权限,引用计数,拥有者,所属组,ACM时间,int blocks[NUM]等),大小是128字节(一般而言,一个文件,一个inode)(文件文件名不在inode保存)。
注:
①在linux中文件的属性中,不包含文件的名称,只知道文件的编号。查找一个文件仅需找到它的inode编号,从而在它的inode表中找到inode,从而找到其blocks数组,根据数组中记载的块号按顺序读出文件内容。
②block数组规定:分为直接索引和简介间接索引。
直接索引:存储文件内容。
间接索引:不存储文件内容,而是继续存储我们的文件版号,从而创建更大的文件。
Block bitmap比特位的位置和块号映射起来,比特位的内容表示该块是否被使用。
inode bitmap:比特位的位置和inode的编号映射起来,比特位的内容表示inode是否有效。
问:上一个文件的时候用不用把块(文件内容)清空呢?
答:不用。根据文件inode编号,找到inode bitmap(发现位图有效),转而去找inode table,读取其属性获得对应的inode和数据块地址关系,读取所有块号,在bitmap和block map里所有的块号全部清零,其次,根据inode编号找到其bitmap由1置为0。
Group Descriptor Table(不会在每个组都存在):描述整个分组基本的使用情况:一共有多少个组,每个组的大小,每个组的inode数量,每个组的block数量,每个组的起始inode,文件系统的类型与名称等。
Super Block:文件系统的信息:里面包含的是整个分区的基本使用情况。
格式化:每一个分区在被使用前,都必须提前先将部分文件系统的属性信息提前设置进对应的分区中,方便我们后续使用这个分区或者分组。
2、文件理解:
①新建一个文件,系统要做什么?
通过新建路径确定分区,在该分区内分配inode——查询Group Descriptor Table是否还有未使用的inode,然后扫描inode bitmap位图结构并读取,选择最近的未使用的编号,将该比特位由0置1。未来可通过inode编号确定分区。在inode Table内找到inode,将文件属性填入。在Block bitmap确认写入数据量的大小,遍历未使用的块,将对应的块号填充到inode属性中特定的下标里,然后直接跳转到对应的块中,把内容填充进去。
②删除一个文件,系统要做什么?
根据文件所处目录,确定文件所处分区,根据inode范围确定分组,用自己inode编号减去起始inode编号,进而可以在inode bitmap进行索引,根据inode编号在位图中对应的比特位由1置0,根据inode将Block Bitmap对应比特位由1置0。(删除=允许被覆盖)。
③查找一个文件,系统要做什么?同理。
④修改一个文件,系统要做什么?同理。
3、如何理解目录
①Linux系统中,一个文件,一个inode,每一个inode都有自己的inode编号(inode的设置,是以分区为单位的,不能跨分区)
inode表示文件的所有属性,但是文件名并不属于inode内的属性!
②问:可是我怎么知道一个文件的inode编号?使用者从来没关心过inode,用的是文件名。
答:通过目录查找。
③目录也是文件,也有自己的inode,也有自己的内容和属性。目录数据块中存储文件的文件名和对应文件inode的映射关系。
④同一个目录下不能有同名文件,因为key值不能相同。文件名冲突系统就无法找到指定文件了。
⑤目录下,没有w,我们无法创建文件。即便创建了文件,文件名与inode的映射关系也无法写到目录文件的数据块里。
⑥目录下,没有r,我们无法查看文件。无法读取目录所对应数据块的文件名与inode的映射关系无法得到。
⑦目录下,没有x,我们就无法进入这个目录。cd目录名本质上是找到目录的inode,但没有x,无法进入目录,无法更新当前目录环境变量。
⑧问:可是目录是文件,也有inode的编号。怎么找?
答:查任何一个目录使,都要从当前目录开始向上递归找到根目录(此文件文件名确定/),找到该数据块,里面的所有文件子目录数据都可以找到。(绝对路径)
⑨Linux系统中,会把用户最常访问的若干目录路径信息通过dentry缓存。
四、软硬链接:
软链接是一个独立的文件,具有独立的inode
硬链接不是一个独立的文件,因为它没有独立的inode。
1、如何理解硬链接?
所谓建立硬链接,本质其实就是在特定目录的数据块中新增文件名和指向的文件的inode编号的映射关系(类似于取别名)。
任意一个文件,无论是目录,还是普通文件,都有inode。
每一个inode内部,都有一个叫做引用计数的计数器(有多少个文件名指向该文件)(默认引用计数为1,自己指向自己)。
目录里保存的是文件名:inode编号的映射关系(默认引用计数为2,有自己目录的文件名和inode映射关系)(目录内部的.文件是该目录的一个硬链接)。
2、如何理解软链接?
软链接是一个独立的文件,有独立的inode,也有独立的数据块,他的数据块里面保存的是指向文件的路径(类似于快捷方式)。
3、为什么要用软链接?
任意路径软件在系统中能找到的/usr/bin目录下建立软连接,就能不带路径执行。
4、为什么要用硬链接?
通常用来进行路径定位,采用硬链接,可以进行目录间切换。硬链接能够维持Linux整体的目录结构。
注:Linux不允许用户对目录建立硬链接,因为会引发环路问题。(而系统可以,比如.和..)。
五、内存管理简述
1、内存的本质是对数据的临时存取,所以我们在系统层面上把内存看作一个大型的缓冲区。内存中的大部分数据未来都会加载到磁盘里或者释放。物理内存以页框为单位和磁盘以页帧为单位进行数据交互(4KB)。
2、问:为什么要以4KB的方式进行拷贝?
答:
①减少IO的次数/减少访问外设的次数---硬件。
②基于局部性原理的预加载机制---软件。
3、操作系统如何管理内存?
操作系统提供了虚拟地址空间的概念,用户在应用层只能看见虚拟地址,但操作系统也能看到内存的物理地址。操作系统管理内存时,先描述,再组织——用struct page结构体描述page必要的属性信息,存储到struct page mem_array数组中(其中数组的下标称为页号),对内存的管理变成了对数组的管理!
用户要访问一个内存时,只需要先直接找到这个4KB对应的page,就能在系统中找到对应的物理页框。所有申请内存的动作都是在访问内存page数组。
申请内存的本质是检测数组中结构体里flag标志位是否被使用,若未使用则将page的比特位置1即可。
4、伙伴系统算法,slab分派器(了解即可)。
5、文件页缓冲:Linux中我们的每个进程打开的每一个文件都要有自己的inode属性和自己的文件页缓冲区。数据通过基数树/基树(字典树)写入到文件页缓冲区。文件的内容是有偏移量的!可以通过文件内容偏移量按照比特位构成字典序,在字典树中找到相应的文件偏移量与内存中配置对应的映射关系。从而让文件有序地进行刷新。
6、IO子系统:操作系统层面上会提供一个公共的队列IO request queue,所有的上层进程最终将数据通过文件写到内存中,文件中的配置构建成struct request结构体封装到队列里。操作系统所谓的刷新就是将队列里的数据依次提交给磁盘。同时也可以通过IO排序,IO合并优化该过程。
六、动静态库
1. 回顾:站在库的制作者角度。
libXXX.a---静态链接
libYYY.so---动态链接
把我们提供的方法给别人用,可以把源文件直接给他,也可以把我们的源文代码想办法打包成库(库+.h)
静态库原理:将.c文件编译成.o文件,将.o文件打包成.a文件,将main.c文件编译为.o文件,两者一结合为最后的可执行程序。
//mymath.h#pragma once#include <stdio.h>extern int myerrno;int add(int x, int y);
int sub(int x, int y);
int mul(int x, int y);
int div(int x, int y);
//mymath.c
#include "mymath.h"int myerrno = 0;int add(int x, int y)
{return x + y;
}int sub(int x, int y)
{return x - y;
}int mul(int x, int y)
{return x* y;
}int div(int x, int y)
{if(y == 0){myerrno = 1;return -1;}return x / y;
}
//Makefilelib=libmymath.a$(lib):mymath.oar -rc $@ $^ //$@ $^表示这些文件的起始和尾
mymath.o:mymath.cgcc -c $^ //$^:形成同名的.o文件.PHONY:clean
clean:rm -rf *.o *.a lib.PHONY:output
output:mkdir -p lib/includemkdir -p lib/mymathlibcp *.h lib/includecp *.a lib/mymathlib
[root@hecs-210801 lesson27]# ll
total 12
-rw-r--r-- 1 root root 228 Nov 26 10:19 Makefile
-rw-r--r-- 1 root root 251 Nov 26 10:10 mymath.c
-rw-r--r-- 1 root root 148 Nov 26 10:09 mymath.h
[root@hecs-210801 lesson27]# make
gcc -c mymath.c
ar -rc libmymath.a mymath.o
[root@hecs-210801 lesson27]# make output
mkdir -p lib/include
mkdir -p lib/mymathlib
cp *.h lib/include
cp *.a lib/mymathlib
[root@hecs-210801 lesson27]# ll
total 24
drwxr-xr-x 4 root root 4096 Nov 26 10:27 lib
-rw-r--r-- 1 root root 1880 Nov 26 10:27 libmymath.a
-rw-r--r-- 1 root root 228 Nov 26 10:19 Makefile
-rw-r--r-- 1 root root 251 Nov 26 10:10 mymath.c
-rw-r--r-- 1 root root 148 Nov 26 10:09 mymath.h
-rw-r--r-- 1 root root 1704 Nov 26 10:27 mymath.o
[root@hecs-210801 lesson27]# tree lib
lib
├── include
│?? └── mymath.h
└── mymathlib└── libmymath.a2 directories, 2 files
删除lib的指令:
[root@hecs-210801 lesson27]# rm -rf lib
未来我们要把库给别人时仅需提供lib文件夹即可。
在其他目录下使用该库时所包头文件为:
#include “lib/include/mymath.h”
或者让系统在指定目录下找头文件,库文件:
[root@hecs-210801 lesson27]# gcc main.c -I ./lib/include/ -L ./lib/mymathlib/ -lmymath
2、站在库的使用者角度:
①第三方库往后使用的时候,必定要用gcc -l。
②深刻理解errno的本质。
③gcc链接程序时,默认是动态链接的。如果系统中只提供静态链接,gcc则只能对该库进行静态链接。
④如果系统中需要链接多个库,则gcc可以链接多个库。
3、解决下载不到静态库的方法:
①拷贝到系统默认的库路径/lib64/usr/lib64/(库的安装)。
②在系统默认的库路径/lib64/usr/lib64/建立软链接。
4、解决下载不到动态库的方法:
①拷贝到系统默认的库路径/lib64/usr/lib64/(库的安装)。
②在系统默认的库路径/lib64/usr/lib64/建立软链接。
③将自己的库所在的路径,添加到系统的环境变量LD_LIBRARY_PATH中。
④/etc/ld.so.conf.d建立自己的动态库路径的配置文件,然后重新ldconfig即可。
(实际情况,我们用的库都是别人的,成熟的库都采用直接安装到系统的方式。)
5、使用外部库:ncurses--基于终端的图形界面的库。
6、动态库是怎么被加载的。
①动态库在进程运行的时候,是要被加载的(静态库没有),这就是动态库具有可执行权限的原因。
②常见的动态库,被所有相关的可执行程序动态链接,都要使用动态库/共享库。所以,动态库在系统中加载之后,会被所有进程共享。
③动态库被加载的方式为:建立映射,从此往后我们执行的任何代码都是在我们的进程地址空间中进行执行。
④事实上,系统在运行中,一定会存在多个动态库,先描述再组织由OS管理起来。系统中,所有库的加载情况,OS都非常清楚。
7、程序加载前后地址:
①编译好没有加载前的程序是有地址的,其可执行数据区可以被分成很多段。形成可执行程序的编址已经形成了平坦模式(严格遵照地址空间的方式编码),因为编译器也要考虑操作系统。
注:可执行程序的地址为逻辑地址(磁盘中程序形成时所对应的地址,存偏移量)。
②程序加载后的地址进程:当可执行程序加载到内存的时候,每条指令天然的具有物理地址。
③执行程序形成的时候包含入口地址entry(逻辑地址)。
④CPU读取可执行程序时直接将entry加载到内部寄存器(EIP/PC)中,找到正文代码段从而找到页表,此时页表未建立对应的映射,触发缺页中断,于是程序被加载进来,具有了物理地址,从而页表也能填上对应的虚拟地址和物理地址,从而在物理内存中找到对应的指令代码。CPU内读取到的指令内部可能有数据,可能也有地址(虚拟地址)。(CPU读到指令中的地址全是虚拟地址)
8、动静态库的地址:
①问题:动态库在共享区过大,那具体映射到哪里呢?
动态库被加载到固定地址空间中的位置是不可能的。库只需在虚拟内存中任意位置加载,并让让自己内部函数不要采用绝对编址,只表示每个函数在库中的偏移量即可。
fPIC:产生位置无关码(直接用偏移量进行编址)。
②静态库为什么不谈加载,不谈与位置无关?
因为静态库已经拷贝在可执行程序里了,直接按绝对编址编码。
9、虚拟地址最终是怎么转换到物理地址的?
相关内容将会在后续章节讲解。
相关文章:

Linux篇:文件系统
一、共识原理: 文件文件内容文件属性 磁盘上存储文件存文件的内容(数据块)存文件的属性(inode) Linux的文件在磁盘中存储是将属性和内容分开存储的。 二、硬件简述: 1. 认识硬件 磁盘:唯一的一…...

AI - Crowd Simulation(集群模拟)
类似鱼群,鸟群这种群体运动模拟。 是Microscopic Models 微观模型,定义每一个个体的行为,然后合在一起。 主要是根据一定范围内族群其他对象的运动状态决定自己的运动状态 Cohesion 保证个体不会脱离群体 求物体一定半径范围内的其他临近物…...

<JavaEE> Java中线程有多少种状态(State)?状态之间的关系有什么关系?
目录 一、系统内核中的线程状态 二、Java中的线程状态 一、系统内核中的线程状态 状态说明就绪状态线程已经准备就绪,随时可以接受CPU的调度。阻塞状态线程处于阻塞等待,暂时无法在CPU中执行。 二、Java中的线程状态 相比于系统内核,Java…...

正则表达式 通配符 awk文本处理工具
目录 什么是正则表达式 概念 正则表达式的结构 正则表达式的组成 元字符 元字符点(.) 代表字符. 点值表示点需要转义 \ r..t 代表r到t之间任意两个字符 过滤出小写 过滤出非小写 space空格 [[:space:]] 表示次数 位置锚定 例:…...
三、ts高级笔记,
文章目录 18、d.ts声明文件19、Mixin混入20、Decorator装饰器的使用21、-高级proxy拦截_Reflect元储存22、-高级写法Partial-Pick23、Readonly只读_Record套对象24、高阶写法Infer占位符25、Inter实现提取类型和倒叙递归26、object、Object、{}的区别27、localStorage封装28、协…...

二十一、数组(6)
本章概要 数组排序Arrays.sort的使用并行排序binarySearch二分查找parallelPrefix并行前缀 数组排序 根据对象的实际类型执行比较排序。一种方法是为不同的类型编写对应的排序方法,但是这样的代码不能复用。 编程设计的一个主要目标是“将易变的元素与稳定的元素…...

flask依据现有的库表快速生成flask实体类
flask依据现有的库表快速生成flask实体类 在实际开发过程中,flask的sqlalchemy对应的model类写起来重复性较强,如果表比较多会比较繁琐,这个时候可以使用 flask-sqlacodegen 来快速的生成model程序或者py文件,以下是简单的示例&a…...

.NET6 开发一个检查某些状态持续多长时间的类
📢欢迎点赞 :👍 收藏 ⭐留言 📝 如有错误敬请指正,赐人玫瑰,手留余香!📢本文作者:由webmote 原创📢作者格言:新的征程,我们面对的不仅仅是技术还有人心,人心不可测,海水不可量,唯有技术,才是深沉黑夜中的一座闪烁的灯塔 !序言 在代码的世界里,时常碰撞…...
链表K个节点的组内逆序调整问题
链表K个节点的组内逆序调整问题 作者:Grey 原文地址: 博客园:链表K个节点的组内逆序调整问题 CSDN:链表K个节点的组内逆序调整问题 题目描述 LeetCode 25. Reverse Nodes in k-Group 本题的 follow up 是: Fol…...

安卓隐私指示器学习笔记
最近了解到Google 在Android12上新增了权限指示器,可以在信号栏的右侧显示当前访问录音机和Camera的应用,点击后可以跳转到相应应用的权限界面,消费者可以控制权限的开启和关闭。国内手机厂商最近几年都在增加隐私看板供能,消费者…...

【Jenkins】jenkins发送邮件报错:Not sent to the following valid addresses:
jenkins报错未能发送邮件到指定邮箱 注意:这是在系统配置中修改 在系统配置》邮件通知中添加配置信息 注意:这个是在项目的配置下修改 配置完成后,重新执行发送邮件成功!!!...

CSS3制作3D爱心动画
1、什么是CSS css,即层叠样式表的简称,是一种标记语言,有浏览器解释执行用来使页面变得更美观。 2、选择器 css3中新增了一些选择器,如下: 3、新样式 边框 css3新增了三个边框属性,分别是: bo…...

Python Opencv实践 - 全景图片拼接stitcher
做一个全景图片切片的程序Spliter 由于手里没有切割好的全景图片资源,因此首先写了一个切片的程序spliter。 如果有现成的切割好的待拼接的切片文件,则不需要使用spliter。 对于全景图片的拼接,需要注意一点,各个切片图片之间要有…...

echarts 几千条分钟级别在小时级别图标上展示
需求背景解决效果ISQQW代码地址strategyChart.vue 需求背景 需要实现 秒级数据几千条在图表上显示,(以下是 设计图表上是按小时界别显示数据,后端接口为分钟级别数据) 解决效果 ISQQW代码地址 链接 strategyChart.vue <!--/** * author: liuk *…...

操作系统的中断与异常(408常考点)
为了进行核心态和用户态两种状态的切换,引入了中断机制。 中断是计算机系统中的一种事件,它会打断CPU当前正在执行的程序,转而执行另一个程序或者执行特定的处理程序。中断可以来自外部设备(如键盘、鼠标、网络等)、软…...

linux下的工具---vim
一、了解vim 1、vim是linux的开发工具 2、vi/vim的区别简单点来说,它们都是多模式编辑器,不同的是vim是vi的升级版本,它不仅兼容vi的所有指令,而且还有一些新的特性在里面。例如语法加亮,可视化操作不仅可以在终端运行…...

代码随想录算法训练营第六十天|84. 柱状图中最大的矩形
LeetCode 84. 柱状图中最大的矩形 题目链接:84. 柱状图中最大的矩形 - 力扣(LeetCode) 和接雨水还挺像的。 代码: #python class Solution:def largestRectangleArea(self, heights: List[int]) -> int:heights.insert(0, 0…...

P14 C++局部静态变量static延长生命周期
目录 01 前言 02 变量的作用域与生命周期 2.1 什么是作用域: 2.2 什么是变量的生命周期: 03 局部静态 3.1非静态变量例子 3.2静态变量例子 04 全局变量 05 后话 01 前言 在前几期里,我们了解了static关键字在特定上下文中的含义。 …...

C语言:写一个函数,求字符串的长度,在main函数中输入字符串并输出其长度(指针)
分析: 在程序中,定义一个函数 fix,该函数使用指针变量来访问字符串中的每个字符,并计算出字符串的长度。fix 函数的参数为指向 char 类型的指针变量 p,表示需要计算长度的字符串。 在主函数 main 中,定义一…...

CentOS7安装Docker运行环境
1 引言 Docker 是一个用于开发,交付和运行应用程序的开放平台。Docker 使您能够将应用程序与基础架构分开,从而可以快速交付软件。借助 Docker,您可以与管理应用程序相同的方式来管理基础架构。通过利用 Docker 的方法来快速交付,…...

铭豹扩展坞 USB转网口 突然无法识别解决方法
当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...

业务系统对接大模型的基础方案:架构设计与关键步骤
业务系统对接大模型:架构设计与关键步骤 在当今数字化转型的浪潮中,大语言模型(LLM)已成为企业提升业务效率和创新能力的关键技术之一。将大模型集成到业务系统中,不仅可以优化用户体验,还能为业务决策提供…...
进程地址空间(比特课总结)
一、进程地址空间 1. 环境变量 1 )⽤户级环境变量与系统级环境变量 全局属性:环境变量具有全局属性,会被⼦进程继承。例如当bash启动⼦进程时,环 境变量会⾃动传递给⼦进程。 本地变量限制:本地变量只在当前进程(ba…...

MFC内存泄露
1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...
Cesium1.95中高性能加载1500个点
一、基本方式: 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...

YSYX学习记录(八)
C语言,练习0: 先创建一个文件夹,我用的是物理机: 安装build-essential 练习1: 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件,随机修改或删除一部分,之后…...

基于当前项目通过npm包形式暴露公共组件
1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹,并新增内容 3.创建package文件夹...
Qt Http Server模块功能及架构
Qt Http Server 是 Qt 6.0 中引入的一个新模块,它提供了一个轻量级的 HTTP 服务器实现,主要用于构建基于 HTTP 的应用程序和服务。 功能介绍: 主要功能 HTTP服务器功能: 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...

DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI
前一阵子在百度 AI 开发者大会上,看到基于小智 AI DIY 玩具的演示,感觉有点意思,想着自己也来试试。 如果只是想烧录现成的固件,乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外,还提供了基于网页版的 ESP LA…...

PL0语法,分析器实现!
简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...