当前位置: 首页 > news >正文

文件系统--底层架构(图文详解)

一、文件系统的底层存储与寻址 

当我们谈到文件系统的底层结构时,最关键的问题是:文件的数据与元数据(属性)如何存储在磁盘上,以及系统是如何定位这些数据的?在谈及文件系统之前,我们要先对储存文件的硬件有一些基本了解

首先,我们来看一下传统磁盘的基础结构 

我们可以看到这种传统的机械磁盘的组成,那么它是如何运转的呢,又是如何进行文件里的内容存储的呢?

首先,计算机是不是只能识别二进制语言啊,而这个二进制语言说起来又有些广泛,有人说二进制不就是0和1吗? 0和1又是什么呢?其实啊,0和1也只是相对的一种概念,它也可以由我们自己替代,比如阴和阳?水和火?这些相对的概念也可以称之为0和1,因此在磁盘中,磁盘由于具有磁性,所以就代表他有南北极,我们就可以近似的把它的磁性之分代表对应的0和1,通过修改它的磁场,是不是就可以达到对“0”和“1”的修改呢?也就可以轻松完成数据的存储。

那具体它又是如何通过以上图片里的这些电子元件完成我们的目的的呢?以下是它的工作步骤:

有同学看到这里就可能没太看懂,那我们来总结以下,大致如下:

磁盘呢其实是一个里面结构,它每个面都是光滑的,都是可读可写的,我们家用的以前的DVD,只是单面且只读的,也就是在盘片高速旋转的过程中,磁头通过左右摇摆和前后移动来到达对应的位置然后改变它下面盘片的磁性,从而达到对数据进行更改存储,那么问题来了,它怎么知道我要改的区域是哪里,这么大一个盘片,怎么能精确的我所要更改的数据存储在哪个区域呢,因此我们来细致的讲解以下盘片的区域划分:

这里我们就要提到一些概念:

首先是磁道 ,一个大的盘面,可以由若干个同心圆由内向外扩散,这每一个圆圈都叫做一个磁道,而这个磁道由圆心开始﹐在同一表面上分别画出无数条半径﹐然後每两条半径所分割的磁轨﹐我们称为磁区(Sector),也就是扇区,所以最终经过层层确认,每个扇区内的那一小块就是我们要寻找的地方。

因此,传统磁盘内如何找到一个指定位置的扇区?

也就是我们的“CHS定址法

a. 首先确定我们在第几个磁头(Header),也就是第几个盘片

b.找到磁头后确定对应的磁道(Cylinder)

c.找到磁道后确认在哪个扇区(Sector)

通过以上步骤我们就可以找到文件所对应在磁盘的准确位置了,是不是很神奇,这是不是也意味着

文件的大小归根结底就是占用多少个扇区的地方嘛!

可如今随着科技的发展,我们所采用的已经不是这种很老的磁盘了,一般企业会采用这种,因为它不会挪动,拿来做服务器刚刚好,一旦做成便携式笔记本,磁头和盘片一接触直接就会造成不可避免的数据损失,我们如今所采用的是固态硬盘(SSD),无需运动,并且有更高的效率。

那么话归正传,既然我们已经知道如何在磁盘里寻找对应的文件地址了,是不是意味着每次我都要进行计算啊,这完全依赖于硬件配置啊,难道我作为一家企业要为每一个不同磁盘规格都编译一份操作系统吗,这肯定不行,因此我们要封装一下这个步骤,让我们的操作系统不需要去依赖硬件才能找到地址,那我们的操作系统是如何做的呢?

之前我们是不是说过,机械磁盘里盘面其实是链接在一起的,大家小学时应该都见过这个东西

它内部其实就是类似两个盘片,中间用线将他们连接起来,反复播放,其实我们的磁盘也同理,n个盘片是不是也可以以线性的方式将他们连接到一起啊!

这是不是相当于将他们都整合成了一个近似的数组啊,这个数组里存放的是不是就是对应的扇区啊!

 也就是说我只需要存放对应位置的下标,然后进行一些列的计算就可以转换成机械硬盘所需要的CHS地址啊,而我操作系统内部就可以按照计算方式来进行管理,是不是很方便

假设我们当前一共有1000个扇区,10个磁道,那么此时我们的对应下标是500,那么现在我们要计算出在第几个磁头,然后转化成当前盘面的对应扇区下标,然后计算出在哪个磁道后计算出对应扇区,步骤如下

通过这样的计算我们就可以成功找到对应的位置了

可是我们可以思考一下,一个扇区一个扇区的计算,对于操作系统的消耗是不是太繁琐了啊,因为扇区的单位实在是太小了,每次都要去计算出这个很小的精确位置十分消耗资源,因此操作系统在于磁盘交互的时候其基本单位是4kb,也就是八个扇区的大小。

操作系统和文件系统通常使用“块(block)”作为基本分配和访问单位。一个块可能由若干扇区组成(例如4KB=8个512字节扇区)。文件系统通过块号(Block Number)来定位文件数据,从而屏蔽底层扇区的复杂定位。假设有N个连续的块可以使用,则文件系统在逻辑上将磁盘空间看成一个块数组(block0, block1, block2, ...)。通过块号,我们可以快速找到对应的扇区,进而从磁盘中读写数据。 如图所示

所以也意味着我们只需要一个起始地址,磁盘的总大小,就可以知道磁盘每个单位的下标,然后通过计算就可以取到对应的CHS地址!

所以也就是现代方法“逻辑区块地址(Logical Block Address, LBA)”

LBA是非常单纯的一种定址模式﹔从0开始编号来定位区块,第一区块LBA=0,第二区块LBA=1,依此类推。这种定址模式取代了原先操作系统必须面对存储设备硬件构造的方式。最具代表性的首推CHS(cylinders-heads-sectors,磁柱-磁头-扇区)定址模式,区块必须以硬盘上某个磁柱、磁头、扇区的硬件位置所合成的地址来指定。CHS模式对硬盘以外的设备来说没什么作用(例如磁带或是网络存储设备),所以通常也不会用在这些地方。过去MFM(Modified Frequency Modulation, 改良调频式)和RLL(Run Length Limited)存储设备都曾使用CHS模式,ATA-1设备更将延伸CHS(Extended Cylinders-Heads-Sectors, ECHS)也派上了用场。

那仅仅到这里就完成我们文件系统的管理了吗?

我们的整个磁盘大小可是有800GB啊,难道每次就对这800GB的磁盘大小进行挨个查询吗,这显然也是不现实的,因此我们的操作系统采取了“分治思想

要管理好我们的磁盘,其实内存是很大的,这个时候我们采用的就是分区,800GB分成若干个区域,有管理好200GB的机制,就可以以此类推管理好剩余区域,分区下面再分组,就完成了文件系统的管理

大型磁盘往往被分割为多个分区(Partition),每个分区作为独立的逻辑存储区域,安装(mount)为独立的文件系统。例如,一个1TB磁盘可以分成200GB、200GB、300GB、100GB等多个分区。这样做的目的是“分而治之”,简化管理和提升灵活性。

在类UNIX文件系统(如ext系列)中,分区内部还分成块组(block group)。块组内包含inode表、块位图、inode位图以及数据块区域。通过将文件系统分块组管理,可以提升文件系统的本地性和查找效率。

如图所示,Linux ext2 文件系统,上图为磁盘文件系统图(内核内存映像肯定有所不同),磁盘是典型的块设备,硬盘分区被划分为一个个的block 。一个 block 的大小是由格式化的时候确定的,并且不可以更改。例如 mke2fs -b 选项可以设定block 大小为 1024 2048 4096 字节。而上图中启动块( Boot Block )的大小是确定的

那我们每个分组内的这些信息都代表什么呢?

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

 我们之前说过Linux内文件是由什么组成的?

文件 = 内容 + 属性 ,那么文件在磁盘中的存储就是文件的内容+文件的属性数据,本质上都是数据,存储在磁盘里,一个正常的文件,就要有他的对应的属性啊,这个属性就是inode,也就是一个正常文件就要有对应的inode属性集合。

那么先说我们的inode,

inode表是存放文件元数据(属性)的区域。这里的inode是一个结构体,记录了文件的:

  • 文件大小(以字节为单位)
  • 文件所有者和所属组ID
  • 文件权限和类型(普通文件、目录、符号链接等)
  • 文件的时间戳(访问、修改、变化时间)
  • 指向文件数据块的索引指针(直接、间接、双重间接、三重间接指针)等信息

需要注意的是,在Linux的ext2/3/4文件系统中,inode并不存放文件名。文件名存储在目录的data block中,而inode指针是通过目录项(directory entry)将文件名映射到inode号(number)上。

inode Bitmap(inode位图)

inode位图(inode Bitmap)与块位图类似,只不过它是标记inode的使用情况。

  • 1表示对应的inode已被使用(对应某个文件或目录)
  • 0表示该inode空闲可用

当需要创建新文件时,文件系统会在inode位图中查找空闲inode(即bit为0的位置),然后分配给新文件,并将该位清为1表示已占用。

Block Bitmap(块位图)

块位图(Block Bitmap)是用于记录本块组中哪些数据块已经被分配,哪些还空闲的位图数据结构。每一位(bit)对应一个block:

  • 1位代表该block已被占用
  • 0位代表该block空闲可用

当文件需要分配新的数据块时,文件系统会在对应块组的Block Bitmap中找到一个置0的bit,将其置1,并分配给文件。由于是位图结构,查找和修改都很高效,只需简单的位操作即可。

数据区(Data Area)

其次是我们的Data Blocks,这就是我们的数据区,也就是存放文件的内容的地方,里面的一个个基本单位就是4KB,其实就是我们磁盘最开始一个个小扇区单元块,完全一致。实际上我们在寻找文件时必须用inode号来寻找,inode是以分区为单位分配的而不是以分组的形式分配的,每个组只需要记录自己的起始inode和结束inode即可,也就是在上层确定一个inode后直接在下面的分组确定自己在哪个分组里就行,可是问题是我们每个分组里不都是inode要从0开始吗,这也怎么区分呢?其实可以用inode号减去当前分组的起始inode,剩下的数字就可以进去bitinode查看是否被占用和后续使用了.

超级块(Super Block)

超级块(Super Block)是整个文件系统的“全局信息表”,其中记录了文件系统的关键信息,包括但不限于:

  • 文件系统总的block数量和可用block数量
  • 文件系统总的inode数量和可用inode数量
  • 每个block的大小、每个inode的大小
  • 文件系统创建时间、最近挂载/写入/检查时间
  • 文件系统的特性标志(例如是否有日志扩展)
  • 检查间隔(每隔多少次挂载或多少时间后需要进行fsck)等信息

在ext2中,超级块不仅在分区开头的特定位置存储,还会在每个Block Group中存放一份副本(除非格式化时指定了不存放备份)。这些副本作为冗余信息存在,一旦主超级块损坏,可以从块组的副本中恢复文件系统的结构信息。

如果超级块信息被全部破坏,那么文件系统的结构信息就无法找回,整个文件系统相当于“失去了地图”。

可是到这里为止,大家有没有想过,inode编号的存在我们已经掌握了,但是你真正对文件进行操作的时候什么时候用inode号了,是不是都说以文件名的方式来进行操作啊,跟inode有什么关系呢?

那么到了这里我们就不得不谈一个相关的概念了:目录

我们说Linux里是不是一切皆文件啊,我要组织这些文件,那我目录是不是就担任这个角色呢,所以目录也有自己的内容+属性,他的属性是不是和inode差不多,内容呢,我们之前说过inode里不存文件名,其实文件名是存放到目录里的,我们通过目录里文件名对应的inode来进行寻找文件,其实是文件名和inode建立了一层映射关系,让我们可以通过目录直接分配对应的Inode来进行后续的管理啊,所以我们可以引申出一些结论

一个目录下不能建立同名文件的关系是什么,是不是无法区分对应的是哪个inode号啊,

而查文件的本质就是 文件名-> inode号

所以对一个文件没权限写的本质是什么,是不是没有访问inode的权利啊,找不到映射关系怎么写入呢?

所以我们每次访问是不是都要从目录里进行访问,那么我们的目录不也是文件吗?

目录名不也是一个inode吗,他也有对应的数据块啊,所以实际上我们在访问一个路径下的文件时,其实是对路径进行一个逆向解析,先找到根目录,然后通过根目录的inode找到下一个目录的Inode,紧接着一步步在inode里找inode,最后找到文件的inode就可以进行操作了,所以根目录的inode一定是刚打开操作系统的时候就已经确定好了,

这些操作都是操作系统自己做的

这就是为什么我们在打开文件时都必须要有路径!

一个文件访问前都是先访问目录,这个目录就已经确定好分区了

所以目录是谁提供的呢,进程在启动时就已经携带了,都是由操作系统提供的

为了提高效率,Linux内核有dentry(directory entry)缓存,用于缓存最近解析的路径信息。这样在多次访问同一路径时,无需重复进行层层目录查找,能显著减少系统开销。

这些组件是如何协同工作的?

  1. 查找文件时的步骤
    当我们通过路径名(如/home/user/file.txt)访问文件时,操作系统会从根目录开始:

    • 根目录的inode号通常是固定的(比如2号)。内核根据根目录的inode从inode表中加载该inode信息(通过超块和GDT可以快速找到inode表位置)。
    • 根据该inode的块指针找到存放该目录内容的data block,读取其中的目录项(文件名与inode号的映射)。
    • 找到home目录名对应的inode号,接着打开home目录的inode,同理找到user目录inode号,再找到file.txt的inode号。
    • 一旦确定了file.txt的inode号,就通过inode表读取file.txt的inode信息,从而得到该文件的数据块指针,最终从数据区中读取该文件的内容。
  2. 分配新文件时的步骤
    当新建一个文件时,文件系统需要:

    • 从inode位图中找到一个空闲inode号,分配给新文件,并在inode表中初始化文件的属性信息。
    • 如果文件需要写入数据,为文件分配数据块。即在块位图中找到一个空闲块,将其标记为已占用,并写入文件数据内容到该块中。
    • 将新文件的名称与其inode号写入父目录的数据区(即父目录文件的data block中),使得路径解析时能正确找到它。
  3. 在内核内存映射中的对应关系
    在内核中,超级块信息会被读入内存的struct super_block,GDT会读入struct group_desc结构数组中,inode表的信息会被内核通过struct inode对象描述,目录项会通过struct dentry缓存,数据块会映射到页缓存中(page cache)以减少频繁磁盘I/O。
    虽然内存中结构体与磁盘上的数据结构不完全相同,但逻辑对应关系保持一致,内核通过特定的读写操作和缓冲机制(buffer head、page cache)来将磁盘数据结构映射和加载到内存中,进行缓存和同步。

下面将通过一个较为完整、详细的流程来描述当我们在用户程序中使用 fwrite() 向一个新建文件中写入数据时,在底层发生了什么。从用户态的C标准库调用开始,一直到数据最终被写入到磁盘文件系统的实际块中,以及新文件创建过程中的元数据分配与更新流程。

#include <stdio.h>
#include <stdlib.h>int main() {FILE *fp = fopen("newfile.txt", "w");if (fp == NULL) {perror("fopen");return 1;}const char *data = "Hello, fwrite!\n";size_t wrote = fwrite(data, 1, sizeof("Hello, fwrite!\n") - 1, fp);if (wrote < sizeof("Hello, fwrite!\n") - 1) {perror("fwrite");}fclose(fp);return 0;
}

实验过程分解

1. 文件创建(fopen阶段)

当你执行 fopen("newfile.txt", "w") 时,底层流程大致如下:

  1. 用户态C库层面
    fopen()是C标准库函数,它并不直接完成文件创建,而是调用系统调用(如open())完成底层操作。此时:

    • fopen()会根据模式"w"决定调用open("newfile.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666)系统调用(精确标志可能略有差异)。
    • 若文件不存在,传入的 O_CREAT 标志会使内核尝试新建文件。
  2. 内核VFS层与文件系统层面
    内核接收到open()系统调用后,会进行路径解析:

    • 从根目录开始查找newfile.txt所在目录(比如当前工作目录),通过目录项(dentry)和inode查找目标目录的inode。
    • 找到该目录的inode后,内核在该目录的数据块中搜索newfile.txt的目录项。如果文件不存在,内核就要创建一个新的inode来表示新文件。
  3. inode与block分配(新文件诞生)
    在ext2文件系统中,新文件创建需要分配一个空闲的inode:

    • 内核通过对应块组的inode位图(inode bitmap)找到一个空闲的inode号,将其标记为已用。
    • 在inode表(inode table)中初始化该inode结构:设置文件类型为普通文件、初始大小为0、时间戳、权限(0666受umask影响),链接计数等。
    • 不需要立即分配数据块,空文件初始大小为0;数据块将在后续写入时分配(延迟分配策略在ext4中更明显,在ext2中则可能马上分配第一个块)。

    同时,内核需要在目标目录的数据块中添加一个新的目录项:

    • 目录的数据块是一个文件名到inode号的映射。
    • 内核找到该目录文件的数据块,在合适位置插入"newfile.txt"对应的目录项(包含文件名和新分配的inode号)。

    目录项更新后,newfile.txt就被记录在文件系统中,并且它有自己的inode,但文件内容还为空。

  4. 返回文件描述符与FILE指针
    内核为此新文件分配一个file对象(struct file),并在进程的文件描述符表中加入一条指向该file对象的记录。open()系统调用返回一个整数文件描述符(fd)。
    C库的fopen()接收到fd后,会为其分配一个FILE *结构,设置用户态缓冲区并返回给用户。至此,fp指针就有效地指向了一个新创建的空文件。

2. 写入数据(fwrite阶段)

当执行 fwrite(data, 1, len, fp) 时,发生了如下过程:

  1. 用户态C库缓冲
    fwrite()首先将要写入的数据拷贝到fp所管理的用户态输出缓冲区(这是C标准库提供的缓冲区)。此时并未发生真正的内核写入。
    fwrite()的返回值表示成功写入用户态缓冲区的字节数。

    如果用户调用fflush(fp)或者缓冲区满、文件关闭(fclose)或者程序结束后,才会实际调用write()系统调用将数据从用户态缓冲写入内核态。

    若使用的是行缓冲或全缓冲,fwrite写入的数据可能先暂存在用户态,不立即刷新到内核。

  2. 刷入内核(write系统调用)
    fclose(fp)或缓冲策略触发刷新时,C库将调用write(fd, buffer, length)系统调用。

    内核处理:

    • 内核接收write()系统调用,使用文件描述符找到对应的struct file对象。
    • struct file通过f_inode成员可找到对应的inode。
    • 文件系统(ext2)检查该文件inode。如果当前文件大小为0,还没有分配数据块,则需要为该文件分配数据块:
      • 内核通过Block Bitmap查找一个空闲数据块,将其标记为已用。
      • 在inode结构中更新i_block[]或间接块指针信息,指向分配的新数据块。
    • 将用户传递的写入数据(通过copy_from_user)拷贝到内核页缓存(page cache)对应的数据页中。页缓存是内核中缓存文件内容的区域,以减少频繁的磁盘IO。
    • inode的大小被更新(增加写入的字节数),时间戳(mtime、ctime)更新。
    • 此时数据仍在内核页缓存中,还没有真正写入磁盘。
  3. 缓存与延迟写回
    数据先写入内核的页缓存,这提高了性能,因为内核可以合并多次写入,将其在合适时机一次性写入磁盘。
    写回由内核的同步机制触发(pdflush或kworker线程)或在特定条件下(同步文件系统、文件关闭、缓冲区满、调用fsync())执行。

3. 文件系统同步与落盘

当内核需要将数据实际写入磁盘(可能是延迟一段时间后):

  1. 块映射与缓冲区写入
    内核知道文件所在的块组、inode表和数据块位置。需要将数据缓存页中的内容写入对应的磁盘块:

    • 使用块设备驱动,将页缓存中的数据发出I/O请求(通过I/O调度器)传给磁盘控制器。
    • 磁盘控制器将相应的扇区读写操作发给硬盘。
  2. 元数据同步
    为了保证一致性,除了数据块本身外,对inode表、Block Bitmap、inode Bitmap的更改也需写回磁盘。例如:

    • 分配新inode和数据块时修改的位图需要同步到磁盘对应的块。
    • 修改inode表中的文件大小和时间戳信息也会写回。

    在ext2文件系统中,这些元数据的更新没有日志,因此如果系统在更新中崩溃,可能需要fsck来恢复一致性。

4. 文件关闭(fclose阶段)

当你调用 fclose(fp)

  • fclose()会先调用fflush()将用户态缓冲中剩余数据写入内核。
  • 然后调用close()系统调用关闭文件描述符。
  • 内核将减少file对象的引用计数,如果是最后一个关闭该文件的进程,则file对象会被释放。
  • inode和dentry缓存保留一定时间,以便下次访问文件时加快速度,但最终也会被回收。

此时,从应用程序的角度看,文件已经成功写入并关闭。数据最终会在一定时间内被写回磁盘(如果还未写回),从而持久地存储在分区对应的data block中。

总结底层过程

  1. 路径解析与inode分配fopenopen系统调用通过内核的VFS层、dentry、inode找到目录并创建新文件的inode,分配其inode号,并在目录数据块中新建文件名->inode号的目录项。

  2. 用户态I/O缓冲fwrite仅将数据写入用户态缓冲区,不立即到内核。当执行fflushfclose或缓冲策略触发时,才调用write()系统调用。

  3. 内核缓冲(页缓存)与块分配:内核write()将数据写入内核的页缓存中。如需为文件增加空间,会通过Block Bitmap找到空闲块,将其分配给该文件的inode,并更新inode表。inode大小、时间戳更新。这些修改还在内核内存中。

  4. 延迟写回与磁盘同步:内核稍后会将页缓存数据块、更新后的位图块、inode表块写入磁盘中对应的扇区。如果系统调用fsync()或定期同步进程(例如sync)执行,就会强制立即落盘。

  5. 文件关闭fclose()最终导致close()系统调用,内核释放文件描述符和相关数据结构引用。

通过上述过程,从调用fwrite()往新建文件中写入数据,到文件数据真正写入磁盘,中间经过了用户态缓冲—>内核态缓冲—>磁盘的多层抽象,期间涉及路径解析、inode与block分配、目录项更新、位图修改和inode表修改等关键步骤。这就是使用 fwrite 和新建文件这两个实验在底层全面展开的实际流程。

 

总体来说,这些元数据结构(超级块、GDT、位图、inode表)与数据区紧密协作,形成了ext2文件系统的“管理层”(元数据)与“内容层”(数据区),借助块组的分层次管理和位图的高效查找,使得文件系统在底层能够有效组织和管理海量文件与数据,保证文件读写、创建、删除和寻址的高效与可靠。 

 

相关文章:

文件系统--底层架构(图文详解)

一、文件系统的底层存储与寻址 当我们谈到文件系统的底层结构时&#xff0c;最关键的问题是&#xff1a;文件的数据与元数据&#xff08;属性&#xff09;如何存储在磁盘上&#xff0c;以及系统是如何定位这些数据的&#xff1f;在谈及文件系统之前&#xff0c;我们要先对储存…...

温州医院儿童自闭症康复中心:为孩子打开光明未来

在自闭症这一神秘而复杂的神经发育障碍面前&#xff0c;无数家庭曾陷入迷茫与无助。然而&#xff0c;在中国的大地上&#xff0c;有两座灯塔般的存在&#xff0c;它们分别为温州医院儿童自闭症康复中心和广州星贝育园自闭症儿童寄宿制学校&#xff0c;它们用专业的技术和无尽的…...

Tr0ll: 1 Vulnhub靶机渗透笔记

Tr0ll: 1 本博客提供的所有信息仅供学习和研究目的&#xff0c;旨在提高读者的网络安全意识和技术能力。请在合法合规的前提下使用本文中提供的任何技术、方法或工具。如果您选择使用本博客中的任何信息进行非法活动&#xff0c;您将独自承担全部法律责任。本博客明确表示不支…...

网络通信技术

网络通信技术 IP路由基础 什么是路由 路由是指导报文转发的路径信息,通过路由可以确认转发IP报文的路径。路由设备是依据路由转发报文到目的网段的网络设备,最常见的路由设备:路由器。路由设备维护着一张路由表,保存着路由信息。路由的功能 路径选择数据转发、数据过滤维…...

十一、容器化 vs 虚拟化-Docker 使用

文章目录 前言一、Docker Hello World二、Docker 容器使用三、Docker 镜像使用四、Docker 容器连接五、Docker 仓库管理六、Docker Dockerfile七、Docker Compose八、Docker Machine九、Swarm 集群管理 前言 Docker 使用‌ Docker 容器使用、镜像使用、容器连接、仓库管理、Do…...

npm error Error: Command failed: F:\360Downloads\Software\nodejs\node.exe

前言&#xff1a; 电脑环境&#xff1a;win7 node版本&#xff1a;18.20.0 npm版本&#xff1a;10.9.2 情景再现&#xff1a;电脑上是存在的vuevite的项目且可以正常运行。想着摸鱼的时间复习一下ts语法&#xff0c;所以想创建一个demo。按照 开始 | Vite 官方中文文档 官网创建…...

html中,实现通过拖拽调整图像尺寸

<!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <meta name"viewport" content"widthdevice-width, initial-scale1.0"> <title>html中拖拽修改图像尺寸</title> <styl…...

sqlmap详解

一.sqlmap -u URL --forms sqlmap -u http://192.168.11.136:1337//978345210/index.php --forms 针对特定的 URL 进行 SQL 注入测试&#xff0c;特别是针对表单&#xff08;form&#xff09;的 POST 注入 forms&#xff1a;这个参数告诉 sqlmap 解析并测试目标 URL 中的表单…...

浏览器插件开发实战

浏览器插件开发实战 [1] 入门DEMO一、创建项目二、创建manifest.json三、加载插件四、配置 service-worker.js五、以书签管理器插件为例manifest.jsonpopup.htmlpopup.js查看效果 [2] Vue项目改造成插件一、复习Vue项目的结构二、删除、添加个别文件三、重写build [3] 高级开发…...

【特殊子序列 DP】力扣552. 学生出勤记录 II

可以用字符串表示一个学生的出勤记录&#xff0c;其中的每个字符用来标记当天的出勤情况&#xff08;缺勤、迟到、到场&#xff09;。记录中只含下面三种字符&#xff1a; ‘A’&#xff1a;Absent&#xff0c;缺勤 ‘L’&#xff1a;Late&#xff0c;迟到 ‘P’&#xff1a;Pr…...

C/C++流星雨

系列文章 序号直达链接1C/C爱心代码2C/C跳动的爱心3C/C李峋同款跳动的爱心代码4C/C满屏飘字表白代码5C/C大雪纷飞代码6C/C烟花代码7C/C黑客帝国同款字母雨8C/C樱花树代码9C/C奥特曼代码10C/C精美圣诞树11C/C俄罗斯方块12C/C贪吃蛇13C/C孤单又灿烂的神-鬼怪14C/C闪烁的爱心15C/C…...

Docker 安装 Jenkins:2.346.3

准备&#xff1a;已安装Docker&#xff0c;已配置服务器安全组规则 1581 1、拉取镜像 [rootTseng ~]# docker pull jenkins/jenkins:2.346.3 2.346.3: Pulling from jenkins/jenkins 001c52e26ad5: Pull complete 6b8dd635df38: Pull complete 2ba4c74fd680: Pull complet…...

枫清科技高雪峰:从数据到知识,重塑产业智能化的核心驱动力

2024 年 12 月 5 日&#xff0c;由智东西主办的“2024 中国生成式 AI 大会”在上海盛大开幕&#xff0c;汇聚了全球 AI 领域的顶尖专家、行业领袖与技术创新者。枫清科技&#xff08;Fabarta&#xff09;创始人兼 CEO 高雪峰应邀出席&#xff0c;并在大会上发表主题演讲&#x…...

【过滤器】.NET开源 ORM 框架 SqlSugar 系列

目录 0、 过滤器介绍 1、表过滤器 &#xff08;推荐&#xff09; 1.1 手动添加过滤器 1.2 禁用、清空、备份和还原 1.3 联表查询设置 1.4 动态添加 2、修改和删除用过滤器 2.1 局部设置 2.2 全局设置 &#xff08;5.1.4.62&#xff09; 3、子查询用过滤器 4、联表过滤…...

在 Ansys Q3D 中求解直流和交流电感

提取电缆的电感对于确保电气和电子系统的性能和可靠性至关重要。本篇博客文章将介绍使用 Ansys Q3D 求解直流和交流电感的过程。 概述 在这个例子中&#xff0c;我们将考虑一个由两组电缆组成的简单几何&#xff1a;正极和负极&#xff0c;如下所示&#xff1a; 可以使用“自…...

location重定向和nginx代理

文章目录 1 location重定向1.1 概述1.2 rewrite跳转1.3 用例1.4 实验1.4.1 基于域名的跳转1.4.2 基于ip的跳转1.4.3 基于后缀名的跳转 2 nginx的代理2.1 nginx内置变量2.2 实验2.2.1 前提条件2.2.2 正向代理2.2.3 自动代理 1 location重定向 1.1 概述 重定向&#xff1a;就是…...

币安移除铭文市场的深度解读:背后原因及其对区块链行业的影响

引言&#xff1a; 就在昨天&#xff0c;2024年12月10号&#xff0c;币安宣布将移除铭文市场&#xff08;Inscriptions Market&#xff09;。这一消息引发了全球加密货币社区的广泛关注&#xff0c;尤其是在比特币NFT和数字收藏品市场快速发展的背景下。铭文市场自诞生以来迅速…...

【论文复现】基于曲率的图重新布线

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; ❀ 无基于曲率的图重新布线 论文概述核心算法算法说明关键代码 运行方法数据集配置文件训练和测试 运行结果 论文概述 论文链接 Topping, Jake,…...

scala的Array

特性 类型安全&#xff1a;Scala 中的数组是类型安全的&#xff0c;这意味着一旦声明了数组的类型&#xff0c;就只能存储该类型的元素。 大小固定&#xff1a;数组的大小在创建时确定&#xff0c;之后不能改变。 零索引&#xff1a;Scala 数组与 Java 数组一样&#xff0c;都…...

【HarmonyOS实战开发】鸿蒙JS崩溃分析

当未处理的JS异常导致应用意外退出时&#xff0c;应用会生成对应的JS崩溃日志文件&#xff0c;开发者可通过错误日志查看引起崩溃的代码位置及分析应用崩溃的原因。本文将分别介绍JS崩溃分析思路以及典型分析案例。 一、日志信息 以下是崩溃日志信息中对应字段解释。 Device…...

【Vue3】前端使用 FFmpeg.wasm 完成用户视频录制,并对视频进行压缩处理

强烈推荐这篇博客&#xff01;非常全面的一篇文章&#xff0c;本文是对该博客的简要概括和补充&#xff0c;在不同技术栈中提供一种可行思路&#xff0c;可先阅读该篇文章再阅读本篇&#xff1a; FFmpeg——在Vue项目中使用FFmpeg&#xff08;安装、配置、使用、SharedArrayBu…...

与 Cursor AI 对话编程:2小时开发报修维修微信小程序

本文记录了如何通过与 Cursor AI 对话&#xff0c;全程不写一行代码的情况下&#xff0c;完成一个完整的报修小程序。整个过程展示了 AI 如何帮助我们&#xff1a; 生成代码 、解决问题、优化实现、完善细节。 先看一下效果图&#xff1a; 一、项目配置 首先我是这样和 AI 对…...

【机器学习】机器学习的基本分类-无监督学习-主成分分析(PCA:Principal Component Analysis)

主成分分析&#xff08;Principal Component Analysis, PCA&#xff09; 主成分分析&#xff08;PCA&#xff09;是一种常用的降维技术&#xff0c;用于将高维数据投影到低维空间&#xff0c;同时尽可能保留原数据的主要信息&#xff08;方差&#xff09;。 1. PCA 的核心思想…...

工频隔离与高频隔离的优劣对比

工频与高频隔离的优劣以及选择方法的详细介绍&#xff1a; 工频隔离 优点&#xff1a; 隔离效果好&#xff1a;能有效过滤电网上的谐波干扰和负载特性产生的大电流冲击&#xff0c;为负载提供更可靠的保护&#xff0c;适用于对电源稳定性和可靠性要求高的工业、医疗、交通等领…...

前端传入Grule,后端保存到 .grl 文件中

前端传入Grule&#xff0c;后端保存到 .grl 文件中 通过简单的输入框&#xff0c;将Grule的部分拆解成 规则名称 规则描述 规则优先级 规则条件 规则逻辑Grule关键字 when Then 模拟了 if 判断的条件和逻辑部分 类似于 shell 和 ruby 之类的脚本语言&#xff0c;有 then 关键字…...

SpringBoot【十】mybatis之xml映射文件>、<=等特殊符号写法!

一、前言&#x1f525; 环境说明&#xff1a;Windows10 Idea2021.3.2 Jdk1.8 SpringBoot 2.3.1.RELEASE 在利用mybatis进行开发的时候&#xff0c;编写sql时可能少不了>、<等比较符号&#xff0c;但是在mapper映射文件中直接使用是不行的&#xff0c;会报错&#xff0…...

使用Excel 对S型曲线加减速算法进行仿真

项目场景&#xff1a; 项目场景&#xff1a;代码中写了S型加减速算法&#xff0c;相查看生成的加减速数组&#xff0c;直观的展示出来&#xff0c;USB通信一次64字节&#xff0c;对于我几个个32位的频率值不太方便&#xff0c;于是采用Excel进行仿真。 代码中如何生成S加减速曲…...

Java面试宝典 1.13~1.31【2020.5 Beta版】

Java面试宝典 1.13~1.31【2020.5 Beta版】 <a name"14cb060b"></a> 1.Java基础 <a name"22b8b366"></a> 1.13 静态变量与实例变量的区别&#xff1f; 静态变量实例变量定义使用static关键字声明的实例变量在类中声明&#xff0c;但…...

调度系统:使用 Airflow 对 Couchbase 执行 SQL 调度时的潜在问题

使用 Airflow 对 Couchbase 执行 SQL 调度时&#xff0c;通常情况下不会直接遇到与 Couchbase 分布式特性相关的异常&#xff0c;但在某些特定情境下&#xff0c;可能会出现一些与分布式环境、调度和数据一致性相关的潜在问题。以下是一些可能会遇到的问题和建议的解决方案&…...

过载与简单:理解感知

通常情况下&#xff0c;最好的设计是使用最少设计技巧的设计。这是为什么&#xff1f;这一切都是关于人类大脑是如何工作的&#xff0c;它决定了观众对媒体的反应、感受情绪和做出决定。 注意。我们被海量的信息轰炸。不间断地处理所有这些信号会降低我们大脑的注意力。根据微…...

前端加密的方式汇总

目录 一、Base64编码 二、哈希算法 三、对称加密(AES/DES) 四、非对称加密(RSA) 五、加盐 六、Web Cryptography API? 七、总结 随着信息和数据安全重要性的日益凸显&#xff0c;如何保证信息数据在传输的过程中的安全成为开发者重点关注的内容。前端加密通常是指在浏览…...

git新建远程分支后,无法切换

git remote # 列出所有远程主机 git remote update origin --prune # 更新远程主机origin 整理分支 git branch -r # 列出远程分支 git branch -vv # 查看本地分支和远程分支对应关系 git checkout -b gpf origin/gpf # 新建本地分支gpf与远程gpf分支相关…...

HarmonyOS 线性容器List 常用的几个方法

List底层通过单向链表实现&#xff0c;每个节点有一个指向后一个元素的引用。当需要查询元素时&#xff0c;必须从头遍历&#xff0c;插入、删除效率高&#xff0c;查询效率低。List允许元素为null。 List和LinkedList相比&#xff0c;LinkedList是双向链表&#xff0c;可以快速…...

【21天学习AI底层概念】day2 机器学习基础

按照由浅入深的顺序&#xff0c;下一步学习 机器学习&#xff08;Machine Learning&#xff09; 的基础是最自然的选择。机器学习是人工智能的核心技术之一&#xff0c;很多AI系统都依赖它。以下是学习路线建议&#xff1a; 第二步&#xff1a;机器学习基础 学习目标&#xff…...

简单的Java小项目

学生选课系统 在控制台输入输出信息&#xff1a; 在eclipse上面的超级简单文件结构&#xff1a; Main.java package experiment_4;import java.util.*; import java.io.*;public class Main {public static List<Course> courseList new ArrayList<>();publi…...

TDengine Flink集成

Flink 集成 TDengine 主要涉及在 Flink 项目中配置与 TDengine 的连接&#xff0c;实现数据的读取和写入。以下是一个详细的指南&#xff0c;介绍如何在 Flink 中集成 TDengine&#xff1a; 一、准备工作 安装并启动 Flink&#xff1a; 下载并解压 Flink 安装包。启动 Flink …...

数据结构和算法-05堆和优先队列-01

堆结构(heap) 生活中我们遇见的数据结构-堆&#xff1a; 查看电影口碑排名第一的电影。 堆的概念 [堆(heap)] 是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵树的数组对象。堆总是满足下列性质&#xff1a; 堆中某个结点的值总是不大于或不小于其父结点的…...

PDFMathTranslate,PDF多语言翻译,批量处理,学术论文,双语对照(WIN/MAC)

分享一个非常实用的PDF文档翻译项目——PDFMathTranslate。作为一个经常逛GitHub的开发者&#xff0c;我总喜欢翻看各种项目附带的论文&#xff0c;虽然大多时候是瞎研究&#xff0c;但却乐在其中。该项目能够完美保留公式、图表、目录和注释&#xff0c;对于需要阅读外文文献的…...

爬虫基础与实践

爬虫技术基础与实践 在当今数字化的时代&#xff0c;数据成为了宝贵的资源。爬虫技术作为获取数据的重要手段&#xff0c;受到了广泛的关注和应用。本文将介绍爬虫的基本概念、工作原理以及一些常用的技术和工具。 一、爬虫的基本概念 爬虫&#xff0c;也称为网络蜘蛛或网络机器…...

uni-app Android平台上架要求的隐私政策提示配置方法【跨端开发系列】

文章目录 前言&#x1f4d6;一、前言二、DCloud 数据采集说明三、配置方式3.1 HBuilderX3.2.1及以上版本配置方式3.2 HBuilderX3.2.0及以下版本配置方法3.3 模板提示框3.4 无提示框 四、离线打包配置方式五、模板提示框六、二次确认提示框七、国际化八、隐私协议内容需要注意的…...

在Ubuntu上使用docker compose安装N卡GPU的Ollama服务

在现代计算环境中,利用 GPU 进行计算加速变得越来越重要。下面将讲解如何在Ubuntu上使用docker compose安装N卡GPU的Ollama服务。 1、安装 NVIDIA 容器工具 首先,需要确保你的系统已经安装了 NVIDIA 容器工具 nvidia-container-toolkit。这是让 Docker 容器访问 GPU 的关键…...

中文分词学习

1.安装 jieba 库 !pip install jieba jieba 库是用于中文分词的工具&#xff0c;它通过精确的分词算法来处理文本。通过分词可以将中文句子拆分成单独的词语&#xff0c;这对于自然语言处理任务非常重要&#xff0c;比如文本分类、情感分析、关键词提取。 2.中文文本分词处理…...

Seata 分布式事务

1. 分布式事务介绍 传统单体应用场景下&#xff0c;系统的数据保存在一个数据库实例中&#xff0c;通常场景的关系数据库都能自动提供事务保证&#xff0c;并且这种情况下的事务称为本地事务&#xff0c;能保证原子性、一致性、隔离性、持久性&#xff08;ACID 特性&#xff09…...

Burp入门(10)-IP伪造插件

声明&#xff1a;学习视频来自b站up主 泷羽sec&#xff0c;如涉及侵权马上删除文章 感谢泷羽sec 团队的教学 视频地址&#xff1a;IP伪造和爬虫审计_哔哩哔哩_bilibili 本文详细介绍IP伪造插件Burp Fake IP使用。 一、插件安装 打开Burp Suite。进入扩展标签页。点击添加&…...

idea连接SQL Server数据库_idea连接sqlserver数据库

4.设置密码&#xff08;这一步可以在安装数据库时就可以完成&#xff09;&#xff0c;如果觉得用户名有问题&#xff0c;也可以修改用户名 5.查看SQL Server端口号&#xff08;默认端口&#xff1a;1433&#xff09;&#xff0c;选择SQL Server2019配置管理器 6.打开SQL Server…...

SQL汇总数据:聚集函数

我们经常需要汇总数据而无需实际检索出这些数据&#xff0c;为此SQL提供了专门的函数。使用这些函数&#xff0c;SQL查询能够高效地检索数据&#xff0c;以便进行分析和报表生成。这类检索的例子包括&#xff1a; 确定表中行数&#xff08;或者满足某个条件或包含某个特定值的…...

Next.js系统性教学:服务器操作与数据变更

更多有关Next.js教程&#xff0c;请查阅&#xff1a; 【目录】Next.js 独立开发系列教程-CSDN博客 目录 1. 什么是服务器操作和数据变更&#xff1f; 1.1 服务器操作 (Server Actions) 1.2 数据变更 (Mutations) 2. Next.js中的服务器操作与数据变更 2.1 引入&#xff1a…...

Python Selenium 各浏览器驱动下载与配置使用(详细流程)

大家好啊&#xff01;我是NiJiMingCheng 这是我的博客&#xff1a;NiJiMingCheng 这节课我们来学习安装selenium和对应的各个浏览器驱动&#xff0c;个人比较喜欢使用谷歌浏览器驱动&#xff0c;所以接下来以谷歌浏览器来为大家做示例&#xff01;&#xff01;&#xff01; Sel…...

python flask 框架模块介绍

Flask 是一个轻量级、可扩展的 Python Web 框架&#xff0c;特别适合构建小型和中型应用程序。它的设计哲学是简单、灵活&#xff0c;允许开发者根据需要选择或创建功能模块。以下是 Flask 框架的核心模块和其功能的详细讲解&#xff1a; 1. Flask 核心模块 (1) flask.Flask 类…...

手把手搭建基于.NET 8.0的Web API项目

1.背景 工作以后&#xff0c;大部分时间都是在做基于业务的CRUD工作&#xff0c;软件产品或者项目的框架基本都早就搭建好了&#xff0c;程序员只需要在框架内去填格子打代码就行了。于是&#xff0c;我抽了时间来搭建个简单的三层架构模式的web api项目&#xff0c;技术点大概…...