Linux 文件 IO 管理(第三讲:文件系统)

news2024/11/17 17:28:42

Linux 文件 IO 管理(第三讲:文件系统)

  • 进程为什么默认要打开文件描述符为 0,1 和 2 的文件呢?
  • 文件系统
    • 物理磁盘
      • 简单认识
      • 存储结构
      • 对磁盘存储进行逻辑抽象
      • 分组 —— 文件系统
        • Block Bitmap
        • inode Table
        • inode Bitmap
        • GDT(Group Descriptor Table)
        • Super Block
        • 格式化
      • 文件系统细节
        • inode number
        • datablocks[N] 数组
        • 编号唾手可得?
        • 理解文件的增删查改
        • 逆向路径解析和如何找到文件自己所在分区

进程为什么默认要打开文件描述符为 0,1 和 2 的文件呢?

我们写的程序,本质上都是 对数据进行处理(计算,存储等等),既如此就肯定有三个问题:

  • 数据从哪里来
  • 数据去哪里
  • 用户要不要看到这个过程

程序变成进程之后数据并不全是硬编码而成的,也有比如 scanfcin 的数据等等,所以是为了更好地让进程获取数据,动态地让用户看到进程的结果,毕竟这些现象都是和人来往的

归结到底,还是人有这个需求罢了,是历史的原因

文件描述符2标准错误文件 为什么也要被打开呢?

其实 标准错误文件 对应的文件也是 显示器文件,咱可以来验证一下:

int main()
{
    fprintf(stdout, "fprintf hello stdout\n");
    fprintf(stderr, "fprintf hello stderr\n");
    return 1;
}

咱们分别往 标准输出和错误 打印数据,但结果就是两条打印结果都在显示器上

所以在刚开始,文件描述符为 12 的两个下标其实指向同一个文件罢了;如果你 重定向,那也只是重定向 文件描述符1 的下标,所以 标准错误 仍然会往显示器打印,如下:

[exercise@localhost redirection]$ ./Test > log.txt
fprintf hello stderr
[exercise@localhost redirection]$ cat log.txt
fprintf hello stdout
[exercise@localhost redirection]$ 

那为什么还要有 2 呢?
程序运行输出的消息无非就是 正确错误 两类

而正常 Debug 的时候,会将 正确 的调试信息往 1 里打印,错误 的调试信息往 2 里打印,未来我们只需要做一次 重定向 就可以 将正确和错误的调试信息分开

./Test 1>ok.log 2>err.log

正确调试信息 都在 ok.log 文件里,错误调试信息 都在 err.log 文件里

要是想把两种信息写在别的文件里,可以这样:

./Test 1>all.log 2>&1

如此就都在 all.log 文件里了

文件系统

之前谈到的都是被打开的文件,但是磁盘上的大量文件里,被打开的只是少量文件,还有大量没有被打开的啊!

没有被打开的文件是在 磁盘 内存放,所以这种文件也被叫做 磁盘文件

可是你要打开某个文件都是要先找到这个文件,也就是在大容量磁盘里寻找此文件,所以必须要有 文件路径 + 文件名 才能在偌大的磁盘空间里找到此文件

没有被打开的文件 无非就是要放在磁盘中存放,还是那句话,存放的意义就是有朝一日可以更好的取走,所以在本质上就是在 研究文件如何存取的问题

物理磁盘

简单认识

计算机只认识二进制是公认的,而 0 ,1 是被规定出来的,其表示形式可能大不相同,可能使用高低电平表示,也有可能使用磁极表示,所以在物理上会有不同的表现

磁盘拆开就发现里面会有圆形反光的结构,叫做 盘片盘片可读可写可擦除 ,一片盘片两面都可以存数据

接续拆,会发现不止一个盘片,而是一摞盘片,是由很多盘片组合而成的结构,盘片越多,容量越大

而每一个盘面都会有一个磁头(一面一个磁头),磁头通常是用于在特定的盘面当中来回寻址

磁盘的本质是一个机械设备 ,一般磁盘在加电工作的时候,盘片会在类似马达的带动下高速旋转,而磁头会进行左右摆动,由于速度极快,所以磁头和盘片不能紧挨着,不然会造成两个硬件不可逆的损伤,所以 磁头其实是悬浮在盘面上的

存储结构

怎么存就怎么取,这是很正常的想法,所以必须要了解磁盘的内部结构

我们知道磁盘内部有一摞 盘片,每一个 盘片 的正反两面都可以写数据,而每一面都会配备一个 磁头,所以,要想精准找到想要的数据,就要确定数据存放在哪个 磁头

每一个 盘面 又被划分为一圈圈同心圆,这一圈圈就是 磁道,如果从一摞盘片的角度看,相同半径的磁道会构成 柱面,而一旦找到了数据在哪个 磁头 下,就可以确定数据在那一圈 磁道(柱面)

盘面 上一圈圈 磁道 并非磁盘的 最小读写单位,因为人们又将盘面均等的过圆心分开,那么一圈圈磁道就被分为好多 扇区,这 扇区 才是磁盘最小的 读写单位,也就是说不论读取修改与否,都是要将一整个 扇区 的内容送进内存

那么现在要想确定数据的位置,只需要知道 磁头(Header),柱面(Cylinder),扇区(Sector) 的编号即可(CHS定址法

对磁盘存储进行逻辑抽象

其实无论是内存还是外存,我们对其抽象均为 线性结构 ,磁盘内盘面虽然是一圈圈磁道,但拉直依然是直线结构,所以 磁盘整体的抽象结果就是线性的

想象你现在把一圈圈磁道拉直了,变成了直线,那现在唯一可以度量这条直线的就只剩下扇区了,而每一个扇区(sector)的大小均固定,那是不是就可以抽象成为 数组 啊,基本单位就是 扇区 sector disk_array[N] ,而数组会有自己的 下标,那么 在无形之中就相当于为每一个扇区完成编址

那怎么 将这个数组里的扇区下标转换为CHS 地址 呢?其实每一个盘片里的空间都是一样大的,扇区大小数量也一样,非常均等,所以可以通过计算找出 CHS 参数:

假设一块磁盘里,每一个盘面共有 N 个扇区,M 个磁道,那么每个磁道里就有 N / M 个扇区,而 index 是任意扇区在 sector disk_array[N] 里的索引下标

首先要明白,上面的数组是抽象出来的,而真正磁盘的每一个盘面都是从 0 开始标号的,并不是接着上一个盘面编号,不然还抽象什么呀

// num 表示一个磁道有几个扇区
num = N / M;
// 计算位于哪一个盘面,即磁头编号
Header = index / N;
// 计算出所在盘面后,使用 temp 存储 index 编号处于该盘面的下标
temp = index % 1000;
// 利用 temp 直接计算出磁道编号
Cylinder = temp / num;
// 同样 temp 取模得到扇区编号
Sector = temp % num;

这时 index 地址完美映射成为 CHS 地址

上面的工作其实是 磁盘内部直接完成的(比较简单),所以 OS 使用的一直都是抽象出来的虚拟磁盘地址

所以目前为止,文件 = 数组内很多个 sector 的下标内容构成 ,只需要记录下该文件所占的扇区下标,将其送往磁盘,再经过映射,即可完美定位磁盘文件位置

上面的问题是解决了,可是磁盘的一个扇区大小为 512 字节(现在可能是 4KB),OS 觉得太小;如果系统只是需要小小的 4KB 的数据,那 IO 端口就得完成来回 8 次拷贝,效率问题凸显!!!

一般而言,虽然磁盘被访问的 基本单位是 0.5 KB OS 未来和磁盘交互的时候,基本单位是 4 KB(后续博文会说明为什么是 4KB),也就是一次性要拿 8 个 sector ,如此提高 IO 效率问题

那么 OS 就不以 0.5 KB 进行访问,而是 4 KB,这 4 KB 是连续的 8 个扇区 sector ,被称之为

既然 OS 不愿意以扇区为基本单位进行抽象,那就 用块来抽象,那么此时 8 个扇区组成一个基本单位为块,使用下标为连续的块进行编址,那么目前为止 文件 = 数组内很多个 块 的下标内容构成

那现在还怎么使用下标来定位磁盘位置啊?很简单啊,现在的一个块是 8 个扇区,那么 下标值乘以 8 就是这个块开头扇区的原来编号,至此于 OS 而言,未来读取数据可以以块为单位

很显然,块的大小既然是固定的,那么现在只需要知道磁盘的总容量,就可以确定抽象后的每一块磁盘地址,有多少块,每个块的块号,如何转移到对应的多个 CHS 地址之类的全都知道

而块的编号叫做 LBA (Logical Block Address)逻辑块地址 ,也就是 LBA block[N] ,以一个数字来描述磁盘空间的地址,以数组的形式组织起来,妥妥的 先描述,再组织,此后 对磁盘的管理就转变为对数组的管理

如果磁盘空间太大不好管理,那么分区就浮出水面,只要把其中的小分区管理好,其他分区使用一样的方法就能实现管理了啊,分区如何实现呢?只需要记住所有分区的开始和结束的 LBA ,如此分区完成

那么现在 文件就是由很多个 LBA 块组成

分组 —— 文件系统

很显然,分完区还是很大,都是以 100GB 为单位的,所以还需要进行分组,分完组一个组的大小 可能 为 10GB ,相同的问题,只要管理好这 10GB 的小组就能管理好分区,进而就能管理好整个磁盘

而上面的一整套思想被称为 分治思想

我们之前就就说 文件 = 内容 + 属性 ,所以文件在磁盘存储,本质上是存储文件的内容数据 + 文件的属性数据,而 Linux 文件系统特定:文件内容和属性分开存储 ,要想理解这些,就得先理解分组后的一个小组,被称为 磁盘级文件系统 的东西:

在这里插入图片描述

一个 Block group 就是一个分组,也就是 磁盘文件系统Linux ext2文件系统):

  • Block Groupext2 文件系统会根据分区的大小将其划分为数个 Block Group ;每个 Block Group 都有着相同的结构组成
  • 超级块(Super Block):存放文件系统本身的结构信息Super Block 的信息被破坏,可以说整个文件系统结构就被破坏了;记录的信息主要有:
    • blockinode 的总量
    • 未使用的 blockinode 的数量
    • 一个 blockinode 的大小
    • 最近一次挂载的时间
    • 最近一次写入数据的时间
    • 最近一次检验磁盘的时间
    • 等其他文件系统的相关信息
  • GDTGroup Descriptor Table):块组描述符,描述块组属性信息
  • 块位图(Block Bitmap):Block Bitmap 中记录着 Data Block 中哪个数据块已经被占用,哪个数据块没有被占用
  • inode 位图(inode Bitmap):每个 bit 表示一个 inode 是否空闲可用
  • i 节点表:存放文件属性 ,如:文件大小,所有者,最近修改时间等
  • 数据区 Data Block :存放文件内容,是整个分组系统里占据空间最大的区域(九成以上) ,里面都是基本单位大小为 4 KB 的数据块,只存储文件的内容 ,每个块都有其块号

注意 文件加载到内存就是以块为单位分批加载,没有即使文件的最后一个块没有占完,而这一整个块也都是此文件的,只是内容不是此文件的内容而已

Block Bitmap

这是 块位图理想情况下Data Block 里有多少个块 Block Bitmap 就会申请多少个比特位,也就是说 Data Block 里的每一个数据块都对应 Block Bitmap 里的一个 bit 位,如此在 Data Block 里,数据块的占用状态就能被表示出来

注意 bit 位的位置也和数据块一一对应,不能有差错,未来想要为新文件分配空间,可以直接扫描 Block Bitmap 位图,查出 bit 位为 0 的数据块分配即可

inode Table

这玩意就是所谓的 i 节点表,这里面宏观上,其实也全都是数据块,但里面保存的是 所有文件的所有属性

Linux 中文件的属性是大小固定的集合体,就是将所有可以准确描述文件的属性集合在一起成为一个结构体,那也就是说一个文件的内容可以不一样大,但 它们的属性结构体一定是一样大的,这是每个文件都要有的,只是不同的文件属性值不同罢了 ,此乃 先描述

那么在内核里就一定存在 struct inode 结构体,可以描述任意一个文件,里面包含文件的所有属性 ,在里面存在一个非常重要的字段 int inode_number;inode 编号,因为在 struct inode 结构体内部是没有文件名属性的,但是在内核层面,每一个文件都要有 inode number ,我们通过 inode 号去标识一个文件

在 Linux 里可以通过 ls -li 指令查看磁盘文件的 inode number

现在我也知道了一个文件的 inode 号,但是我要怎么找到文件内容的位置呢?在结构体里还会存在属性 int datablocks[N] 用于标识该文件占据的所有块号

在 Linux 系统里, struct inode 结构体 大小一般固定为 128 Byte,而现在一个块的大小为 4KB ,那么一个块就能存储 32 个 inode,每一个文件都有一个 inode

那我怎么知道 inode 的使用情况呢?

inode Bitmap

当然啦,和 Block Bitmap 相同的作用:

比特位的位置 表示第几个 inodeinode number);比特位的内容,表示该 inode 是否被占用

有几个 inodeinode Bitmap 里就有几个比特位

GDT(Group Descriptor Table)

块组描述符,顾名思义:描述块组信息

该结构体主要描述当前分组的基本情况(相当具体),如:块大小,共有多少个 inode ,共有多少个 Data Blocks ,有多少个块没有被使用,有多少个 inode 被使用了等等,都会被记录在此结构体中

说白了就是个管理字段,用来管理整个块组的使用情况

Super Block

超级块 :存放文件系统本身的结构信息,这是存放一个分区的基本信息,上面的简介里也提到存放的相关内容

既然是一整个分区的基本内容,那为啥放在 0 号分组里?不应该单独于所有分组进行存放吗?

并不是每一个分组都有 Super Block,一般会根据实际的文件系统,可能存在于 2 ~ 3 个分组里,但是即便有好几个分组里都有,但大家的 Super Block 内容都是一样的

为什么要这么干呢?纯纯浪费空间啊?都是一样的内容有什么好存的呢?

既然是存放一整个分区的使用情况,说明极为重要啊!磁盘是个机械设备,靠磁头和盘片的物理旋转来定位物理空间,而 Super Block 也并不大,可能就是其中的几个扇区,如果因为一些特殊原因把 Super Block 内容刮花,导致数据失真,那后果就是这个分区都会挂掉,这个影响是巨大的!!!

所以虽然没必让整个分区的所有组都有 Super Block ,但咱还是需要选中几个幸运分组来多保存几份 Super Block ,这是出于安全考量,让文件系统更具有健壮性

格式化

现在我们知道了,一块磁盘仅仅分区是不够的,还需要分组;分完组也是不够,还需要在分组后的所有分组里写入上述 Block Group 结构内容来管理数据

那么日后我们在用磁盘的时候,是基于这么一套文件系统之上来新建删除修改等等

而在每一个分区内进行分组,然后写入文件系统的管理数据,这个叫做 格式化!!!

所以啊,格式化的本质: 在磁盘中写入文件系统

文件系统细节

inode number

寻找任何文件只能通过 inode 编号,所以这个必须知道

inode 编号是以分区为单位整体分配的,而不是分组;一个分区内部的任意文件的 inode 编号都不能重复,但两个分区可能会出现重复,所以 inode 不能跨分区访问!!!

inode 被分配的时候,是按照区域进行划分的:在 Super Block 里会记录当前整个分区的 inode 编号范围;在 GDT 里会记录当前整个分组的 inode 编号范围

当拿到一个文件的 inode 编号后,就可以对照分区和分组的范围,确定文件所属的分组位置,找到所属分组后,对照 inode bitmap 合法与否,合法可直接定位 i 节点表找到该文件

当然 Data Block 里的块号也是如此!是基于分区为单位来整体分配的!而且当文件被分配到某个组后,会优先分配该组 Data Block 里的块, 除非此文件非常大,要不然不会跨组存储

datablocks[N] 数组

其实里面就是指向该文件所使用的数据块

这是将文件属性和文件内容存储位置相关联的属性,而这 N 一般为 15

如果是 15 ,那只能映射到 15 个 data block 数据块,那一个文件最大 15 * 4KB = 60 KB 吗?肯定说不通

其实这 15 个空间里面,前 12 个是 直接映射 的,也就是可以直接指向文件存储的数据块编号;后 2 个则是 间接映射 的,可以通过这 2 个来寻找 2 个数据块单元,但这数据块单元里全都是此文件的存储地址,从而完成扩容;而最后一个存的就厉害了,它指向一个数据块单元,但是这个数据块单元里又指向其他的数据块单元,其他的数据块单元才指向真正的文件存储单元

这就很大了,可以直接映射也可以间接映射(3级甚至是4级)

那如果文件容量大于分组空间?当然是可以的,因为只要文件愿意,依然可以让 datablocks[N] 数组指向其他的分组,所以是 支持跨组访问 的!但 非常不建议这么做,因为文件较大,可能不是存在相邻的组中导致同一个文件零碎存储,跨度大,磁盘寻址时间较长,导致效率过低

在这里插入图片描述

编号唾手可得?

我们使用文件可是使用的文件名,但 OS 找文件却是用的 inode number ,很反直觉啊!而且 inode 文件属性里还不包括文件名!怎么回事?

首先用户在电脑里所处位置一定是目录,那目录是文件吗?肯定是!那就有它自己的属性和内容,所以目录也会有自己的 inode ,和普通文件有着相同的属性字段,只是属性的值不一样罢了

属性可以理解,那目录的内容呢?放什么?目录的内容其实放的是:目录名和 inode number 的关系映射

所以每次打开查看一个目录的内容时,都是通过文件名和其编号的映射关系,才找到文件的 inode number

/ 目录是 系统规定 的,是一定可以找到的,所以每次找文件,都是会对文件路径进行逆向路径解析,但这操作是 OS 自己做的,只是 Linux 会为用户缓存常用的路径,不至于每次都要逆向到 / 目录

所以现在就可以解释:

  • 在同一个目录下为什么不能创建同名文件
  • 目录的 r 权限(查看),本质上是是否允许我们 读取 目录的内容(文件名和 inode 号的映射关系)
  • 目录的 w 权限(新建删除),本质上是是否允许我们向目录进行 修改写入 (文件名和 inode 号的映射关系)
理解文件的增删查改

新建文件:在特定的分区中申请一个 inodeSuper Block 也会记录下最近的 inode 编号的分配,确定好分组的编号后再进入此分组中查找 inode bitmap ,寻找为 0 的 bit 位,计算出 inode 编号,并在 inode table 的对应位置填写属性;再去查找 Block Bitmap 寻找空间分配给该文件,并将数据块的地址和属性进行映射;最后将文件内容进行保存,并将 inode 编号返回和文件名进行映射完成新建

查找 就不谈了,有文件名和 inode 编号就很简单; 修改 也是,只是分为修改属性或内容罢了

删除文件:需要在 inode Bitmap 里找到要删的文件的位置,由 1 置为 0 ;再找到 inode 属性里的 datablocks[N] 数组,将对应在 Block Bitmap 的数据块地址由 1 置为 0 ,此时就完成了

所以啊如果一个文件被误删了,只要还没有被覆盖,是可以恢复出来的!

逆向路径解析和如何找到文件自己所在分区

在云服务器上,一般都只有一个盘,查看:

ls /dev/vda

v 代表虚拟,而 /dev/vda1 则是虚拟出来的一个分区,在 Linux 上要访问一个分区是要将一个分区进行挂载的

挂载 意思是说:将磁盘分区和文件系统的一个目录进行关联,未来我们进入一个分区其实是进入指定的一个目录

指令 df -h

在这里插入图片描述

上图红框就是将 /dev/vda1/ 目录挂载

挂载有什么作用呢?就相当于将此分区和目录进行绑定,然后进入该目录,就是在该分区进行文件操作

而不管怎么样,任何文件在被访问之前,一定有目录,只要有目录,对比目录的字符串前缀来确定自己究竟在哪个分区

所以,目录本身除了可以定位文件,还能确定分区

那么找到一个文件就简单了,现在进程提供一个文件的路径,那么路径的末尾就是文件名,需要根据文件名寻找它自己的 inode number ,如何找?需要再上一级目录的文件内容里寻找嘛,那如何获取上一级目录的文件内容?路径里由上级目录名对吧?然后获取它的 inode 编号才能读取需要的文件 inode 编号对吧?那这个目录的 inode 编号又怎么获取?

显然是不是要一直 逆向解析路径,然后一路回退至 / ,此时再返回回来找文件名和 inode 编号的映射即可

其实每一个文件的寻找过程都是这样的,只是会将常用路径进行缓存,所以会效率会比较高

怎么缓存路径?是不是要用数据结构来描述,再将其以树状结构组织起来?没错,这个数据结构在 Linux 里叫做 struct dentry用于缓存路径

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

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

相关文章

Android 利用OSMdroid开发GIS

1、地址 Github地址:https://gitee.com/mirrors/osmdroid Git地址: GitCode - 全球开发者的开源社区,开源代码托管平台 Git下载包地址:Releases osmdroid/osmdroid GitHub 新建项目 osmdroid在线: (1&#xff09…

将Mixamo的模型和动画导入UE5

首先进入Mixamo的官网 , 点击 Character 选择一个模型 (当然你也可以自己上传模型/绑定动画) 然后点击下载 , 这个作为带骨骼的模型 选择FBX格式 , T Pose 直接下载 点击 Animations 选择动画 , 搜索 idle 默认站立动画 点击下载 , 格式选择 FBX , 不带模型只要骨骼 , 帧数选6…

低功耗TF卡:录音笔、领夹麦、电池门铃优选

概述 TF卡,也称为Micro SD卡或TransFlash卡,是一种流行的小型存储卡,广泛应用于手机、平板电脑、相机和其他便携式设备。TF卡的尺寸非常小,便于携带和使用,不会占用太多设备空间。而且TF卡提供多种容量选择&#xff0…

Go基础编程 - 15 - 延迟调用(defer)

延迟调用 defer 1. 特性2. 常用用途3. defer 执行顺序:同函数内先进后出4. defer 闭包5. defer 陷阱 上一篇:泛型 1. 特性 1. 关键字 defer 用于注册延迟调用。 2. defer 调用直到 return 前才被执行。 3. 同函数内多个 defer 语句,按先进后…

第17章 协同式任务切换

第17章 协同式任务切换 在多任务系统中,每个任务都有各自的局部描述符表(LDT)和任务状态段(TSS)。 从任务切换的时机来讲,有两种基本的策略: 协同式:从一个任务切换到另一个任务。需要当前任务主动地请求暂时放弃执行权&#x…

好看的首页展示

代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title><style>/* RESET…

气膜游泳馆:舒适恒温,寒冷季节中的理想游泳场所—轻空间

随着天气逐渐转凉&#xff0c;许多人在秋冬季节减少了户外活动&#xff0c;尤其是游泳。然而&#xff0c;气膜游泳馆为您提供了一种全新的选择&#xff0c;让您即使在寒冷的季节&#xff0c;也能享受畅游的乐趣。凭借其独特的恒温设计和舒适的环境&#xff0c;气膜游泳馆成为了…

计算机毕业设计宠物领养网站我的发布领养领养用户信息/springboot/javaWEB/J2EE/MYSQL数据库/vue前后分离小程序

目录 1.课题背景 2.课题意义 ‌ 3.技术介绍 4.技术性需求 4.1后端服务‌&#xff1a; 4.2 前端展示‌ 5.数据库设计‌&#xff1a; 6.系统性能‌&#xff1a; 7.安全性‌&#xff1a; 8. 功能介绍&#xff1a; 9. 部分代码 1.课题背景 近年来&#xff0c;随着宠物饲养数量…

TDEngine在煤矿综采管控平台中的应用

一、行业背景 智能综采管控平台&#xff0c;是将煤矿综采工作面传感器数据采集&#xff0c;通过可视化界面展示。实现综采工作面的透明化展示&#xff0c;并基于历史的传感器数据进行机器学习的训练&#xff0c;了解工作面周期来压&#xff0c;设备故障检测等数据应用。因此针…

AIGC引领数智未来:企业架构演进的深度解析与实践路径,The Open Group 2024生态系统架构·可持续发展年度大会专题报道

随着人工智能技术的迅猛发展&#xff0c;特别是以ChatGPT、Sora等为代表的AIGC&#xff08;人工智能生成内容&#xff09;技术的爆发&#xff0c;我们正处于通用人工智能&#xff08;AGI&#xff09;时代的前夜。AIGC技术在多个领域展现出近乎甚至超越人类的能力&#xff0c;已…

【LeetCode】动态规划—最小路径和(附完整Python/C++代码)

动态规划—64. 最小路径和 前言题目描述基本思路1. 问题定义:2. 理解问题和递推关系:3. 解决方法:3.1. 初始化:3.2. 边界条件:3.3. 填充 dp 数组:3.4. 返回结果: 4. 进一步优化:5. 小总结: 代码实现Python3代码实现Python 代码解释C代码实现C 代码解释 总结: 前言 给定一个包含…

liunx系统虚拟机

https://mirrors.aliyun.com/centos/7.9.2009/isos/x86_64/ 下载地址 DVD版本 安装vm软件12通过vm软件来创建一个虚拟机空间通过vm软件在创建好的虚拟机空间上&#xff0c;安装我们的centos操作系统使用centos你得需要将鼠标点击进入界面中&#xff0c;但是鼠标会消失&#xf…

杨辉三角-C语言

1.问题&#xff1a; 输出杨辉三角。 2.解答&#xff1a; 对有特点的数&#xff08;每行开头和结束的数都是1&#xff09;进行赋值&#xff0c;给中间的数进行赋值&#xff0c;把上面赋值后的二维数组&#xff0c;遍历输出。 3.代码&#xff1a; #include<stdio.h>//头…

基于SSM商铺租赁系统JAVA|VUE|SSM计算机毕业设计源代码+数据库+LW文档+开题报告+答辩稿+部署教+代码讲解

源代码数据库LW文档&#xff08;1万字以上&#xff09;开题报告答辩稿 部署教程代码讲解代码时间修改教程 一、开发工具、运行环境、开发技术 开发工具 1、操作系统&#xff1a;Window操作系统 2、开发工具&#xff1a;IntelliJ IDEA或者Eclipse 3、数据库存储&#xff1a…

使用session来实现验证码发送功能

一、什么是session&#xff1f; Session由服务器创建&#xff0c;并为每一次会话分配一个Session对象。同一个浏览器发起的多次请求&#xff0c;同属于一次会话&#xff08;Session&#xff09;。首次使用到Session时&#xff0c;服务器会自动创建Session&#xff0c;并创建Co…

大觅网之自动化部署(Automated Deployment of Da Mi Network)

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 本人主要分享计算机核心技…

前端大模型入门:Transformer.js 和 Xenova-引领浏览器端的机器学习变革

除了调用别人的api接口使用transformer技术&#xff0c;你是否想过将大模型在浏览器中运行呢&#xff1f;尤其是WebGPU的出现&#xff0c;性能比WebGL高不少&#xff0c;很多小任务真的不再需要在一个中心运行了。 不少同学买课学python了&#xff0c;但我还是在坚持用js尝试&a…

【Linux实践】实验九:Shell流程控制语句

【Linux实践】实验九&#xff1a;Shell流程控制语句 实验目的实验内容实验步骤及结果1. 变量的定义和使用2. 条件3. 运算4. if 语句5. case 语句6. for 语句7. while 语句8. until 语句9. 遍历复制10. 计算平方 实验目的 1、掌握条件判断语句&#xff0c;如if语句、case语句。…

tomcat的安装,管理与配置

目录 Tomcat 服务部署 1.关闭防火墙&#xff0c;将安装 Tomcat 所需软件包上传到虚拟机 2.安装JDK 3.设置JDK环境变量 4.安装启动Tomcat 5.启动tomcat 6.优化tomcat启动速度 Tomcat 服务管理 systemd 管理控制 supervisor 管理控制 Tomcat 虚拟主机配置 1.创建 sun…

EE trade:黄金 999 和黄金 9999 的区别

黄金&#xff0c; 作为一种珍贵的金属&#xff0c; 一直是人们投资和收藏的对象。 在购买黄金时&#xff0c; 您可能会遇到两种纯度的黄金 —— 黄金 999 和黄金 9999。 这两种黄金有什么区别? 消费者应该如何选择呢? 一、 黄金 999 和黄金 9999 的区别 含金量&#xff1a;…