当前位置: 首页 > 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;我们要先对储存…...

【OCR】——端到端文字识别GOT-OCR2.0不香嘛?

代码&#xff1a;https://github.com/Ucas-HaoranWei/GOT-OCR2.0?tabreadme-ov-file 在线demo&#xff1a;https://huggingface.co/spaces/stepfun-ai/GOT_official_online_demo 0.前言 最早做ocr的时候&#xff0c;就在想如何能做一个端到端的模型&#xff0c;就不用先检测再…...

SkyWalking 和 ELK 链路追踪实战

一、背景 最近在给项目搭建日志平台的时候&#xff0c;采用的方案是 SkyWalking ELK 日志平台&#xff0c;但发现 ELK 日志平台中的日志没有 Trace ID&#xff0c;导致无法追踪代码报错的整体链路。 空哥提示&#xff1a;Trace ID 是分布式追踪中用来唯一标识一个服务请求或事…...

ETCD的封装和测试

etcd是存储键值数据的服务器 客户端通过长连接watch实时更新数据 场景&#xff1a; 当主机A给服务器存储 name&#xff1a; 小王 主机B从服务器中查name ,得到name-小王 当主机A更改name 小李 服务器实时通知主机B name 已经被更改成小李了。 应用&#xff1a;服务注册与发…...

基于大数据爬+数据可视化的民族服饰数据分析系统设计和实现(源码+论文+部署讲解等)

博主介绍&#xff1a;CSDN毕设辅导第一人、全网粉丝50W,csdn特邀作者、博客专家、腾讯云社区合作讲师、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流✌ 技术范围…...

torch.optim.lr_scheduler.ReduceLROnPlateau

torch.optim.lr_scheduler.ReduceLROnPlateau 是 PyTorch 中的一种学习率调度器&#xff0c;主要用于在模型训练过程中根据某些指标&#xff08;如验证损失&#xff09;动态调整学习率。它是一种基于性能指标动态调整学习率的策略&#xff0c;而不是预定义的固定时间调整。 主要…...

Linux 搭建ftp服务

FTP是什么&#xff1f; FTP&#xff08;文件传输协议&#xff0c;File Transfer Protocol&#xff09;是一种用于在计算机之间传输文件的网络协议。它基于客户端-服务器模型&#xff0c;允许用户从远程服务器上传、下载和管理文件。 FTP的主要作用 文件传输&#xff1a;FTP最基…...

阳光电源嵌入式面试题及参考答案

讲一讲声明变量的时候应该注意哪些内容。 在声明变量时,首先要考虑变量的类型。不同的数据类型有不同的用途和占用的存储空间大小。例如,基本数据类型如整型(int)通常占用 4 个字节,用来存储整数;而浮点型(float)用于存储带有小数部分的数字,占用 4 个字节,双精度浮点…...

PS的功能学习(形状、文字、图层)

关于图层 如果是在一个已经有其他图层的文档界面下&#xff0c;拉一张新图进来&#xff0c;就会自动转换成智能对象 注意&#xff0c;放大之后再栅格化&#xff0c;是会根据原本的防矢量图规则放大之后&#xff0c;再变回像素图层&#xff0c;这个变回来的像素图层是“在原像素…...

项目实例_FashionMNIST_CNN

前言 提醒&#xff1a; 文章内容为方便作者自己后日复习与查阅而进行的书写与发布&#xff0c;其中引用内容都会使用链接表明出处&#xff08;如有侵权问题&#xff0c;请及时联系&#xff09;。 其中内容多为一次书写&#xff0c;缺少检查与订正&#xff0c;如有问题或其他拓展…...

Ubuntu 安装 web 服务器

安装 apach sudo apt install apache2 -y 查看 apach2 版本号 apache2 -v 检查是否启动服务器 sudo service apache2 status 检查可用的 ufw 防火墙应用程序配置 sudo ufw app list 关闭防火墙 sudo ufw disable 更改允许通过端口流量 sudo ufw allow Apache Full 开启…...

burp的编解码,日志,比较器

声明&#xff01; 学习视频来自B站up主 **泷羽sec** 有兴趣的师傅可以关注一下&#xff0c;如涉及侵权马上删除文章&#xff0c;笔记只是方便各位师傅的学习和探讨&#xff0c;文章所提到的网站以及内容&#xff0c;只做学习交流&#xff0c;其他均与本人以及泷羽sec团队无关&a…...

2.1、模版语法

2.1.1、插值语法 1、代码示例 <body><!-- 准备容器 --><div id"app"><!-- 在data中声明的 --><!--1、 data中声明的变量 --><h1>{{msg}}</h1><h1>{{sayHello()}}</h1><!-- 不在data中的变量不可以 -->…...

最小二乘法拟合出二阶响应面近似模型

背景&#xff1a;根据样本试验数据拟合出二阶响应面近似模型&#xff08;正交二次型&#xff09;&#xff0c;并使用决定系数R和调整的决定系数R_adj来判断二阶响应面模型的拟合精度。 1、样本数据&#xff08;来源&#xff1a;硕士论文《航空发动机用W形金属密封环密封性能分析…...

【汽车】-- 常见的汽车悬挂系统

汽车悬挂系统是车辆的重要组成部分&#xff0c;其主要功能是连接车轮和车身&#xff0c;减缓路面颠簸对车身的影响&#xff0c;提高行驶的平顺性、舒适性和操控性。以下是常见的汽车悬挂系统类型及其特点&#xff1a; 1. 独立悬挂系统 每个车轮可以独立上下运动&#xff0c;不…...

VMware Workstation Pro 17 下载 以及 安装 Ubuntu 20.04.6 Ubuntu 启用 root 登录

1、个人免费版本 VMware Workstation Pro 17 下载链接怎么找&#xff1f;直接咕咕 VMware 找到如下链接。链接如下&#xff1a;Workstation 和 Fusion 对个人使用完全免费&#xff0c;企业许可转向订阅 - VMware 中文博客 点进去链接之后你会看到如下&#xff0c;注意安装之后仍…...

记录ubuntu22.04重启以后无法获取IP地址的问题处理方案

现象描述&#xff1a;我的虚拟机网络设置为桥接模式&#xff0c;输入ifconfig只显示127.0.0.1&#xff0c;不能连上外网。&#xff0c;且无法上网&#xff0c;用ifconfig只有如下显示&#xff1a; 1、sudo -i切换为root用户 2、输入dhclient -v 再输入ifconfig就可以看到多了…...

linux 删除系统特殊的的用户帐号

禁止所有默认的被操作系统本身启动的且不需要的帐号&#xff0c;当你第一次装上系统时就应该做此检查&#xff0c;Linux提供了各种帐号,你可能不需要&#xff0c;如果你不需要这个帐号,就移走它&#xff0c;你有的帐号越多,就越容易受到攻击。 1.为删除你系统上的用户,用下面的…...

core Webapi jwt 认证

core cookie 验证 Web API Jwt 》》》》用户信息 namespace WebAPI001.Coms {public class Account{public string UserName { get; set; }public string UserPassword { get; set; }public string UserRole { get; set; }} }》》》获取jwt类 using Microsoft.AspNetCore.Mvc…...

【Redis】Redis基础——Redis的安装及启动

一、初识Redis 1. 认识NoSQL 数据结构&#xff1a;对于SQL来说&#xff0c;表是有结构的&#xff0c;如字段约束、字段存储大小等。 关联性&#xff1a;SQL 的关联性体现在两张表之间可以通过外键&#xff0c;将两张表的数据关联查询出完整的数据。 查询方式&#xff1a; 2.…...

Oracle Recovery Tools工具一键解决ORA-00376 ORA-01110故障(文件offline)---惜分飞

客户在win上面迁移数据文件,由于原库非归档,结果导致有两个文件scn不一致,无法打开库,结果他们选择offline文件,然后打开数据库 Wed Dec 04 14:06:04 2024 alter database open Errors in file d:\app\administrator\diag\rdbms\orcl\orcl\trace\orcl_ora_6056.trc: ORA-01113:…...

常用环境部署(二十四)——Docker部署开源物联网平台Thingsboard

1、Docker和Docker-compose安装 参考网址如下&#xff1a; CENTOS8.0安装DOCKER&DOCKER-COMPOSE以及常见报错解决_centos8安装docker-compose-CSDN博客 2、 Thingsboard安装 &#xff08;1&#xff09;在/home目录下创建docker-compose.yml文件 vim /home/docker-com…...

SqlServer Doris Flink SQL 类型映射关系

SqlServer 对应 Flink SQL 数据类型映射关系 SQL Server TypeFlink SQL Typechar(n)CHAR(n)varchar(n)VARCHAR(n)nvarchar(n)VARCHAR(n)nchar(n)VARCHAR(n)textSTRINGntextSTRINGxmlSTRINGdecimal(p, s)DECIMAL(p, s)moneyDECIMAL(p, s)smallmoneyDECIMAL(p, s)numericNUMERIC…...

Java 中的方法重写

在 Java 中&#xff0c;方法重写&#xff08;Method Overriding&#xff09;是面向对象编程的一个重要概念&#xff0c;它指的是子类中存在一个与父类中相同名称、相同参数列表和相同返回类型的方法。方法重写使得子类可以提供特定的实现&#xff0c;从而覆盖&#xff08;或改变…...

v-for遍历多个el-popover;el-popover通过visible控制显隐;点击其他隐藏el-popover

场景:el-popover通过visible控制显隐;同时el-popover是遍历生成的多个。 原文档的使用visible后就不能点击其他地方使其隐藏;同时解决实现点击其他区域隐藏 <template><div><template v-for="(item,index) in arr" :key="index"><…...

从 Excel 文件中读取数据生成 SQL 语句[快捷main方法]

从 Excel 文件中读取数据生成 SQL 语句的实现 在日常工作中&#xff0c;我们经常需要从 Excel 文件中提取数据&#xff0c;并将其转换为 SQL 插入语句&#xff0c;以便于将数据导入到数据库中。在这篇文章中&#xff0c;我将展示如何使用 Java 来实现这一需求。 项目需求 我…...

从0到1实现项目Docker编排部署

在深入讨论 Docker 编排之前&#xff0c;首先让我们了解一下 Docker 技术本身。Docker 是一个开源平台&#xff0c;旨在帮助开发者自动化应用程序的部署、扩展和管理。自 2013 年推出以来&#xff0c;Docker 迅速发展成为现代软件开发和运维领域不可或缺的重要工具。 Docker 采…...

Vue框架入门

Author&#xff1a;Dawn_T17?? 目录 什么是框架 一.Vue 的使用方向 二.Vue 框架的使用场景 &#xff08;TIP&#xff09;MVVM思想 三.Vue入门案例 TIP&#xff1a;插值表达式 四.Vue-指令? &#xff08;1&#xff09;v-bind 和 v-model? ? &#xff08;2&#x…...

vue入门实战(二)父子组件显示,参数传递

经过上次的写法&#xff0c;我们已经写出每个list项&#xff0c;现在要在每个父组件下面加入自己的子项 一、新建子组件&#xff1a; smallItem.vue&#xff1a; <script> export default{props:[text,id,status] //父组件传来的参数 } </script> <template>…...

【Linux】Ubuntu:安装系统后配置

hostname&#xff1a;更改主机名 打开终端。 使用hostnamectl命令更改主机名。 sudo hostnamectl set-hostname 新的主机名你可以使用hostnamectl 命令来验证更改是否成功&#xff1a; hostnamectlChrome&#xff1a;更换默认浏览器 以下是从 Ubuntu 中移除预装的 Snap 版 Fi…...

vb6做网站/苏州疫情最新消息

目录 UCX 的意义 UCX 通信接口简介 支持的传输(协议&#xff09; UCX社区 UCX 编程模型简介 建立连接 内存注册 异步任务处理&#xff08;重点&#xff09; 使用UCX 编译debug版本 构建RPM包 构建DEB 包 构建Doxygen文档 使用UCX安装OpenMPI和OpenSHMEM 使用UCX安…...

英文网站制作 官网/网站收录提交入口大全

最近看了关于卢森堡博士的《非暴力沟通》一书&#xff0c;强烈推荐&#xff0c;这本书很实用。尤其是对于亲近之人之间改善关系&#xff0c;如何化解彼此之间的冷暴力。简单来是两个方面&#xff0c;对外对其他的观察要客观&#xff0c;评论一个人的时候一定要有特定的时间和地…...

国务院网站集约化建设/24小时最新国际新闻

我们每天行走在城市的摄像头下&#xff0c;我们的口袋里装满各种能表明我们身份的卡&#xff0c;我们的个人信息每天暴露在网络等信息平台上……无论我们去哪&#xff0c;不管我们做什么&#xff0c;都似乎有那么一双“眼睛”在看着。无处不在的“第三只眼”&#xff0c;凝结成…...

衡水网站建设03181688/网络公司主要做哪些

动态链接库 静态链接库...

网页留言板模板/关键词seo排名优化推荐

实现如下图所示的一个页面布局 布局框架 思路讲解&#xff1a; 1.一开始的思路是使用android提供的画布将布局中的内容花下来&#xff0c;之后想了想觉得比较麻烦&#xff0c;所以又开启了另外一条思路。 2.如上图“布局框架”所示。最外部是一个大布局&#xff08;RelativeLay…...

南昌网站建设制作与维护/软文标题大全

Pacemaker、corosync pacemaker详细介绍&#xff1a; http://blog.51cto.com/freeloda/1274533 corosync详细介绍&#xff1a; http://blog.51cto.com/freeloda/1272417 Pacemaker是一个集群资源管理器。它利用集群基础构件&#xff08;OpenAIS 、heartbeat或corosync&#xff…...