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

easyfs 简易文件系统

easyfs

  • easyfs 简易文件系统
    • 文件系统
    • 虚拟文件系统 VFS
    • 简易文件系统 easyfs
    • 磁盘布局
      • 超级块
    • easyfs 文件系统结构
    • 磁盘上的索引结构
    • 索引节点
    • `Inode` 和 `DiskInode` 之间的关系
      • 举例说明读取文件的过程( `/hello` )
  • 参考文档

easyfs 简易文件系统

文件系统

常规文件和目录实际都是保存在持久存储设备中的。持久存储设备仅支持以扇区为单位的随即读写,这和我们通常认识的通过路径即可索引到文件进行操作的用户视角有很大的不同。负责两者转换的便是 文件系统

文件系统负责将逻辑上的目录树结构映射到持久存储器上,决定设备上的每个扇区各应存储哪些内容;同时,文件系统页可以从持久存储设备还原出逻辑上的目录树结构。

虚拟文件系统 VFS

一个计算机系统中存在多个持久存储设备,每个存储设备上面的数据可以以不同文件系统格式存储的;所以为了能够向用户提供统一的界面和对这些设备进行统一的管理,内核中增加了一层 虚拟文件系统 VFS

虚拟文件系统规定了逻辑上的目录结构的通用格式以及相关操作的抽象接口。不同的底层文件系统均实现虚拟文件系统要求的那些抽象接口,并挂在到系统中,就可以实现对这些持久存储设备上的不同文件系统的统一管理。

在这里插入图片描述

简易文件系统 easyfs

该文件系统只是为了完整展示文件系统的工作原理。

  • 扁平化:仅存在一个目录 /。在索引一个文件时,直接使用文件名而不关心其路径;
  • 权限控制:全程只有单用户,不区分用户和用户组概念。
  • 不记录文件访问/修改的时间辍。

磁盘的布局体现在磁盘上各个扇区的内容上,而解析磁盘布局得到的逻辑目录树结构则是通过内存上的数据结构来访问的——这表示文件系统涉及到同时对磁盘和对内存的访问:

  • 对内存直接通过一条指令即可直接读写内存相应的位置;
  • 操作磁盘则需要使用软件的方式向磁盘发出请求来间接进行对写;

我们需要特别注意哪些数据结构是存放在磁盘上的,哪些时存储在内存中的——这样才不会引起还乱。

easyfs 本身划分为不同的层次,形成层次花模块设计构架:

  • 磁盘块设备接口层:定义了以块大小为单位对磁盘块设备进行对写的 trait 接口
  • 块缓存层:在内存中缓存磁盘块的数据,避免频繁读写磁盘;
  • 磁盘数据结构层:磁盘上的超级块、位图、索引节点、数据块、目录项等核心数据结构和相关处理;
  • 磁盘块管理器层:合并了上述核心数据结构和磁盘布局所形成的磁盘文件系统数据结构,以及创建/打开文件系统相关处理和磁盘块的分配和回收处理;
  • 索引点层:管理索引节点(文件控制块)数据结构,并实现文件创建/打开/读写等成员函数来支持文件操作相关的系统调用的处理;

块设备接口层

easyfs 最底层声明了一个块设备的抽象接口 BlockDevice :

pub trait BlockDevice: Send + Sync + Any {fn read_block(&self, block_id: usize, buf: &mut [u8]);fn write_block(&self, block_id: usize, buf: &mut [u8]);
}

它实现了两个抽象方法:

  • read_block 可以将编号为 block_id 的块从磁盘读入内存中的缓冲区 buf ;
  • write_block 可以将内存中的缓冲区 buf 中的数据写入磁盘编号为 block_id 的块;

因为块设备仅支持以块为单位进行随即读写,这两个抽象方法向上提供了统一的接口方法;但具体实现需要由块设备驱动实现。

easyfs 的块缓存层会调用这两个方法进行块缓存的管理。

Tips:

块与扇区

扇区(Sector)是块设备随即读写的大小单位,通常扇区为 512 字节。

块(Block) 是文件系统存储文件时的大小单位,每个块的大小等同于一个或多个扇区。

在 easyfs 中块大小和扇区同为 512 字节。

块缓存层

由于操作系统频繁读写磁盘会极大降低性能,因此先通过 read_block 将一个块上的数据从磁盘督导内存缓冲区中,这个缓冲区中的内容是可以直接读写的。如果对于缓冲区中的内容进行了修改,那么后续还需要通过 write_block 将缓冲区中的内容写回到磁盘块中。

从性能上考虑,要尽可能降低实际块读写( read/write_block )的次数,因为每一次调用都会产生大量的开销。要做到这一点,关键在于对块进行读写合并操作。

统一管理块缓冲区:

  • 当要读写一个块的时候,首先区全局管理器中查看这个块是否已经被缓存到内存中;
  • 在一段连续时间内对于一个块进行的所有操作均是在同一个固定的缓冲区中进行的,从而解决同步性问题;
  • 全局管理器会尽可能将更多的块操作合并起来,并在必要的时机发起真正的块读写;
pub const BLOCK_SZ: usize = 512;
pub struct BlockCache{cache: [u8; BLOCK_SZ],block_id: usize,block_device: Arc<dyn BlockDevice>,modified: bool,
}

cache 是一个 512 字节的数组,表示位于内存中的缓冲区 —— 对应于一个磁盘块的大小;

block_id 记录了这个块缓存来自于磁盘中的块的编号;

block_device 保留一个底层块设备的引用时的可以和它打交道;

modified 记录自从这个块缓存从磁盘在如内存之后,它是否被修改过;

每个 BlockCache 保存一个扇区的数据。

磁盘数据结构层

磁盘上的超级块、位图、索引节点、数据块、目录项等核心数据结构和相关处理

磁盘块管理器层

合并了上述核心数据结构和磁盘布局所形成的磁盘文件系统数据结构,以及创建/打开文件系统的相关处理和磁盘块的分配和回收处理

索引点层

管理索引节点(即文件控制块)数据结构,并实现文件创建/文件打开/文件读写等成员函数来向上支持文件操作相关的系统调用的处理

磁盘布局

如何将一个逻辑上的文件目录树结构映射到磁盘上,决定磁盘上的每个块应该存储文件相关的哪些数据。为了更容易进行管理和更新,将磁盘上的数据组织位若干种不同的磁盘上数据结构,并合理安排它们在磁盘中的位置。

easyfs的磁盘布局:

在这里插入图片描述

超级块

#[repr(c)]
pub struct SuperBlock{magic: u32,pub total_blocks: u32,pub inode_bitmap_blocks: u32,pub inode_area_blocks: u32,pub data_bitmap_blocks: u32,pub data_area_blocks: u32,
}

magic 用于验证文件系统合法性的魔数;

total_block 文件系统管理的总块数, 不一定等于磁盘的总块数;

inode_bitmap_blocks 索引块位图区域的长度——索引块位图区用于表示索引块的分配情况;

inode_area_blocks 索引块区域——该区域被索引位图块管理;

data_bitmap_blocks 数据块位图区域的长度——数据块位图区用于表示数据块的分配情况;

data_area_blocks 数据块区域——该区域被数据块位图管理;

​ 超级块是文件系统使用的数据,表示文件系统在磁盘上的布局情况。

  • 索引节点位图区域:索引位图的实际保存区域;
  • 索引节点区域:索引节点的实际保存区域;
  • 数据块位图区域:数据块位图的实际保存区域;
  • 数据块区域:数据块的实际保存区域;

通过超级块可以得到文件系统的整体情况:

easyfs 文件系统结构

pub struct EasyFileSystem{pub block_device: Arc<dyn BlockDevice>,pub inode_bitmap: Bitmap,pub data_bitmap: Bitmap,inode_area_start_block: u32,data_area_start_block: u32,
}
impl EasyFileSystem {/// Open a block device as a filesystempub fn open(block_device: Arc<dyn BlockDevice>) -> Arc<Mutex<Self>> {// read SuperBlockget_block_cache(0, Arc::clone(&block_device)).lock().read(0, |super_block: &SuperBlock| {assert!(super_block.is_valid(), "Error loading EFS!");let inode_total_blocks =super_block.inode_bitmap_blocks + super_block.inode_area_blocks;let efs = Self {block_device,inode_bitmap: Bitmap::new(1, super_block.inode_bitmap_blocks as usize),data_bitmap: Bitmap::new((1 + inode_total_blocks) as usize,super_block.data_bitmap_blocks as usize,),inode_area_start_block: 1 + super_block.inode_bitmap_blocks,data_area_start_block: 1 + inode_total_blocks + super_block.data_bitmap_blocks,};Arc::new(Mutex::new(efs))})}
}

open 函数可以获取一个磁盘上文件系统。从函数中的内容可以看出:

  • 索引位图区是超级块上从 1 到长度为 inode_bitmap_blocks 的位置的内容;
  • 数据位图区是 inode_total_blocks 下一个磁盘块开始长度为 data_bitmap_blocks 的位置的内容;
  • 索引区域开始位置为索引位图区的下一个磁盘块位置;
  • 数据区域开始位置为数据位图区的下一个磁盘位置;

这正好与磁盘布局相对应。

磁盘上的索引结构

#[repr(C)]
pub struct DiskInode {pub size: u32,pub direct: [u32; INODE_DIRECT_COUNT],pub indirect1: u32,pub indirect2: u32,type_: DiskInodeType,
}#[derive(PartialEq)]
pub enum DiskInodeType {File,Directory,
}
  • size 表示文件/目录内容的字节数;
  • type_ 表示索引节点的类型 DiskInodeType ,目前仅支持文件 File 和目录 Directory 两种类型;
  • direct/indirect1/indirect2 都是存储文件内容/目录内容的数据块的索引,这也是索引节点名字的由来:
  • 当文件很小的时候,只需用到直接索引, direct 数组中最多可以指向 INODE_DIRECT_COUNT 个数据块,当取值为 28 的时候,通过直接索引可以找到 14KiB 的内容。
  • 当文件比较大的时候,不仅直接索引的 direct 数组装满,还需要用到一级间接索引 indirect1 。它指向一个一级索引块,这个块也位于磁盘布局的数据块区域中。这个一级索引块中的每个 u32 都用来指向数据块区域中一个保存该文件内容的数据块,因此,最多能够索引 512 / 4 = 128 512/4=128 512/4=128 个数据块,对应 64 K i B 64KiB 64KiB 的内容。
  • 当文件大小超过直接索引和一级索引支持的容量上限 78 K i B 78KiB 78KiB 的时候,就需要用到二级间接索引 indirect2 。它指向一个位于数据块区域中的二级索引块。二级索引块中的每个 u32 指向一个不同的一级索引块,这些一级索引块也位于数据块区域中。因此,通过二级间接索引最多能够索引 128 × 64 K i B = 8 M i B 128×64KiB=8MiB 128×64KiB=8MiB 的内容;

索引节点

easyfs 实现了磁盘布局并能够将所有块有效管理起来。但是对于使用者,往往不关心磁盘布局的实现,而更多的时关注目录树中逻辑上的文件和目录。 Inode 索引节点就是实现这个目的而存在的。

pub struct Inode {block_id: usize,block_offset: usize,fs: Arc<Mutex<EasyFileSystem>>,block_device: Arc<dyn BlockDevice>,
}
  • block_id 是记录磁盘 id 的 ;
  • block_offset 是数据在某个磁盘块中的偏移量;
  • fs 是一个指向 easyfs 的指针;
  • block_device 代表哪一个磁盘设备;

InodeDiskInode 之间的关系

Inode 是存放在内存中的记录文件索引节点信息的数据结构,对应了磁盘上的 DiskInode 结构体的位置;

DiskInode 是磁盘中固定块的数据结构,它是磁盘上每个数据存储位置的索引,对应实际数据存放于物理的块的管理;

从超级块中得到的文件系统信息和索引的 inode_id 可以得到 Inode 数据结构体。

I n o d e . b l o c k _ i d = s u p e r _ b l o c k . i n o d e _ a r e a _ s t a r t _ b l o c k + i n o d e _ i d / i n o d e s _ p e r _ b l o c k Inode.block\_id\ = super\_block.inode\_area\_start\_block\ + inode\_id\ /\ inodes\_per\_block Inode.block_id =super_block.inode_area_start_block +inode_id / inodes_per_block

I n o d e . b l o c k _ o f f s e t = ( i n o d e _ i d % i n o d e s _ p e r _ b l o c k ) a s u s i z e ∗ i n o d e _ s i z e Inode.block\_offset\ =\ (inode\_id\ \%\ inodes\_per\_block)\ as\ usize\ *\ inode\_size Inode.block_offset = (inode_id % inodes_per_block) as usize  inode_size

根据 Inode 结构体中的信息可以得到其数据块对应的磁盘索引的位置。

根据读出的磁盘位置索引 DiskInode 可以根据其中的 directindirect1indirect2 三个字段获取数据块的实际存储位置。

举例说明读取文件的过程( /hello

  • 读取根目录内容,获取 helloinode 信息,获取 hello 的数据索引块号;
  • 根据数据索引块号找到对应的数据实际存储的物理块号;
  • 读取数据信息;

参考文档

《文件系统接口》

《简易文件系统 easy-fs》

相关文章:

easyfs 简易文件系统

easyfs easyfs 简易文件系统文件系统虚拟文件系统 VFS简易文件系统 easyfs磁盘布局超级块 easyfs 文件系统结构磁盘上的索引结构索引节点Inode 和 DiskInode 之间的关系举例说明读取文件的过程&#xff08; /hello &#xff09; 参考文档 easyfs 简易文件系统 文件系统 常规文…...

【架构论文-1】面向服务架构(SOA)

【摘要】 本文以我参加公司的“生产线数字孪生”项目为例&#xff0c;论述了“面向服务架构设计及其应用”。该项目的目标是构建某车企的数字孪生平台&#xff0c;在虚拟场景中能够仿真还原真实产线的动作和节拍&#xff0c;实现虚实联动&#xff0c;从而提前规避问题&#xff…...

刚刚!更新宁德时代社招Verify测评语言理解数字推理SHL题库、网盘资料、高分答案

宁德时代社招入职的Verify测评主要分为两大块&#xff1a;语言理解和数字推理。语言理解部分包括阅读理解、逻辑填空和语句排序&#xff0c;要求在17分钟内完成30题。数字推理部分包括数字序列、数学问题解决和图表分析&#xff0c;同样要求在17分钟内完成18题。这些测评题目旨…...

C++笔记---智能指针

1. 什么是智能指针 1.1 RALL设计思想 RAII&#xff08;Resource Acquisition Is Initialization&#xff0c;资源获取即初始化&#xff09;是一种资源管理类的设计思想&#xff0c;广泛应用于C等支持对象导向编程的语言中。它的核心思想是将资源的管理与对象的生命周期紧密绑定…...

CentOS 7系统中更改YUM源为阿里云的镜像源

引言 更换阿里的镜像源可以带来诸多好处&#xff0c;包括提高下载速度、提升稳定性、同步更新、简化配置、节省带宽资源以及增强系统安全性等。因此&#xff0c;对于使用CentOS系统的用户来说&#xff0c;更换阿里的镜像源是一个值得考虑的选择。 1.备份yum源 mv /etc/yum.r…...

Python酷库之旅-第三方库Pandas(206)

目录 一、用法精讲 961、pandas.IntervalIndex.mid属性 961-1、语法 961-2、参数 961-3、功能 961-4、返回值 961-5、说明 961-6、用法 961-6-1、数据准备 961-6-2、代码示例 961-6-3、结果输出 962、pandas.IntervalIndex.length属性 962-1、语法 962-2、参数 …...

3.4CQU数学实验???

meshgrid 是一个用于生成网格点坐标的函数。它常用于在二维或三维空间中创建坐标网格&#xff0c;用于可视化和数据处理。 在二维情况下&#xff0c;meshgrid 函数接受两个一维数组作为输入&#xff0c;并返回两个二维数组&#xff0c;这两个数组中的元素分别表示了所有可能的…...

Linux(CentOS)开放端口/关闭端口

一、普通用户使用 sudo 操作&#xff0c;开放/关闭端口&#xff0c;80 1、检查端口是否开放 sudo firewall-cmd --zonepublic --query-port80/tcp 2、开放端口 sudo firewall-cmd --zonepublic --add-port80/tcp --permanent 3、重新加载&#xff08;开放或关闭端口后都需…...

GreenDao适配AGP8.7+

升级配置 工具版本Android StudioLadybug 2024.2.1 Path2AGP8.7.2KPG1.8.21GGP3.3.1明细 classpath "com.android.tools.build:gradle:$agp_version"classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kgp_version"classpath "org.greenrobot:g…...

【前端】Typescript从入门到进阶

以下是 TypeScript 的常用知识点总结&#xff0c;涵盖了从基础到入门的内容&#xff0c;并配有代码示例&#xff1a; 1. TypeScript 基础 1.1 安装和配置 安装 TypeScript 并初始化配置文件&#xff1a; npm install -g typescript tsc --init 1.2 基本类型 TypeScript 提供…...

在 RHEL 8 | CentOS Linux release 8.5.2111上安装 Zabbix 6

1. 备份YUM源文件 cd /etc/yum.repos.d/ mkdir bak mv C* ./bak/ wget -O /etc/yum.repos.d/CentOS-Linux-BaseOS.repo https://mirrors.aliyun.com/repo/Centos-vault-8.5.2111.repo yum clean all yum makecache2. 将 SELinux 设置为宽容模式&#xff0c;如下所示。 sudo s…...

光纤HDMI线怎么连接回音壁?

第一步&#xff1a;准备HDMI线、光纤线&#xff08;TOSLINK线&#xff09;、视频源设备、回音壁 第二步&#xff1a;连接HDMI线&#xff0c;找到视频源设备上的HDMI输出口&#xff0c;将HDMI线的一端插入这个接口&#xff0c;再把HDMI线的另一端插入回音壁的HDMI输入口。注意检…...

屏幕后期处理

1、屏幕后期处理效果 屏幕后期处理效果&#xff08; Screen Post-Processing Effects&#xff09;是一种在渲染管线的最后阶段应用的视觉效果&#xff0c;允许在场景渲染完成后对最终图像进行各种调整和效果处理&#xff0c;从而增强视觉体验 常见的屏幕后期处理效果有&#x…...

K8资源之endpoint资源EP资源

1 endpoint资源概述 endpoint资源在K8S中用来表s示vc与后端 Pod 之间的连接关系的对象。当创建svc时&#xff0c;svc根据标签是否相同或svc名字是否和ep名字相同&#xff0c;把svc和ip关联上。 删除svc时&#xff0c;会自动的删除同名的ep资源。 2 ep资源和svc的关联测试 […...

微软日志丢失事件敲响安全警钟

NEWS | 事件回顾 最近&#xff0c;全球最大的软件公司之一——微软&#xff0c;遭遇了一场罕见的日志丢失危机。据报告&#xff0c;从9月2日至9月19日&#xff0c;持续长达两周的时间里&#xff0c;微软的多项核心云服务&#xff0c;包括身份验证平台Microsoft Entra、安全信息…...

Qt生成应用程序exe

1. 将工程用MinGW编译器在release模式下编译&#xff0c;生成可执行文件XXX.exe&#xff0c;新建一个文件夹如&#xff1a;F:\Setup\minGW&#xff0c;把exe文件放到这个目录下。 2. 将该编译器的bin文件添加到PATH环境变量里&#xff1a;bin文件路径为&#xff1a;D:\Qt\Qt5.…...

C#中的HttpContent、HttpClientHandle、HttpWebRequest

C#中的HttpContent 在C#中&#xff0c;HttpContent 是 System.Net.Http 命名空间下的一个类&#xff0c;它是 HttpClient 类用来发送和接收HTTP内容的基础。HttpContent 表示HTTP请求或响应的正文内容&#xff0c;并且可以序列化和反序列化数据。 HttpContent 是一个抽象类&a…...

23.网工入门篇--------介绍一下园区网典型组网架构及案例实践

园区网典型组网架构主要分为小型、中型、大型三种类型&#xff0c;以下是详细介绍及相关案例实践&#xff1a; 小型园区网&#xff1a; 架构特点&#xff1a; 用户规模&#xff1a;适用于接入用户数量较少的场景&#xff0c;一般支持几个至几十个用户。覆盖范围&#xff1a;仅限…...

QT鼠标事件

QT鼠标事件 1.概述 这篇文章介绍如何使用事件和获取事件的信号 2.创建项目 创建一个widget类型项目&#xff0c;在widget.ui文件中添加一个label控件 然后在项目名称上右键选择Add new... 添加文件&#xff0c;选择 C Class 自定义类名Mylabel&#xff0c;选择基类Base …...

Ubuntu 的 ROS 操作系统turtlebot3环境搭建

引言 本文介绍如何在Ubuntu系统中为TurtleBot3配置ROS环境&#xff0c;包括安装和配置ROS Noetic的步骤&#xff0c;为PC端控制TurtleBot3提供操作指南。 安装和配置的过程分为PC设置、系统安装、依赖安装等部分&#xff0c;并在最后进行网络配置&#xff0c;确保PC端能够顺利…...

Android Wi-Fi 连接失败日志分析

1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分&#xff1a; 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析&#xff1a; CTR…...

服务器硬防的应用场景都有哪些?

服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式&#xff0c;避免服务器受到各种恶意攻击和网络威胁&#xff0c;那么&#xff0c;服务器硬防通常都会应用在哪些场景当中呢&#xff1f; 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...

五年级数学知识边界总结思考-下册

目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解&#xff1a;由来、作用与意义**一、知识点核心内容****二、知识点的由来&#xff1a;从生活实践到数学抽象****三、知识的作用&#xff1a;解决实际问题的工具****四、学习的意义&#xff1a;培养核心素养…...

【配置 YOLOX 用于按目录分类的图片数据集】

现在的图标点选越来越多&#xff0c;如何一步解决&#xff0c;采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集&#xff08;每个目录代表一个类别&#xff0c;目录下是该类别的所有图片&#xff09;&#xff0c;你需要进行以下配置步骤&#x…...

LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf

FTP 客服管理系统 实现kefu123登录&#xff0c;不允许匿名访问&#xff0c;kefu只能访问/data/kefu目录&#xff0c;不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...

【C++特殊工具与技术】优化内存分配(一):C++中的内存分配

目录 一、C 内存的基本概念​ 1.1 内存的物理与逻辑结构​ 1.2 C 程序的内存区域划分​ 二、栈内存分配​ 2.1 栈内存的特点​ 2.2 栈内存分配示例​ 三、堆内存分配​ 3.1 new和delete操作符​ 4.2 内存泄漏与悬空指针问题​ 4.3 new和delete的重载​ 四、智能指针…...

【JVM】Java虚拟机(二)——垃圾回收

目录 一、如何判断对象可以回收 &#xff08;一&#xff09;引用计数法 &#xff08;二&#xff09;可达性分析算法 二、垃圾回收算法 &#xff08;一&#xff09;标记清除 &#xff08;二&#xff09;标记整理 &#xff08;三&#xff09;复制 &#xff08;四&#xff…...

MyBatis中关于缓存的理解

MyBatis缓存 MyBatis系统当中默认定义两级缓存&#xff1a;一级缓存、二级缓存 默认情况下&#xff0c;只有一级缓存开启&#xff08;sqlSession级别的缓存&#xff09;二级缓存需要手动开启配置&#xff0c;需要局域namespace级别的缓存 一级缓存&#xff08;本地缓存&#…...

ubuntu系统文件误删(/lib/x86_64-linux-gnu/libc.so.6)修复方案 [成功解决]

报错信息&#xff1a;libc.so.6: cannot open shared object file: No such file or directory&#xff1a; #ls, ln, sudo...命令都不能用 error while loading shared libraries: libc.so.6: cannot open shared object file: No such file or directory重启后报错信息&…...

使用SSE解决获取状态不一致问题

使用SSE解决获取状态不一致问题 1. 问题描述2. SSE介绍2.1 SSE 的工作原理2.2 SSE 的事件格式规范2.3 SSE与其他技术对比2.4 SSE 的优缺点 3. 实战代码 1. 问题描述 目前做的一个功能是上传多个文件&#xff0c;这个上传文件是整体功能的一部分&#xff0c;文件在上传的过程中…...