『 Linux 』文件系统

news2025/1/21 9:32:21

文章目录

    • 磁盘构造
      • 磁盘抽象化
    • 磁盘的寻址方式
    • 磁盘控制器
    • 磁盘数据传输
    • 文件系统
      • Inode
      • 数据块(Data Blocks)
      • 超级块(SuperBlock)
      • 块组描述符(Group Descriptor)
      • 格式化


磁盘构造

请添加图片描述

磁盘内部构造由磁头臂,磁头,主轴,盘片,盘面,磁道,柱面,扇区构成;

  • 磁头臂控制磁头的移动,可以精确地定位到磁盘上的特定磁道;
  • 磁头负责读取和写入数据;在读取数据时,磁头会检测盘片表面的磁性变化;在写入数据时,磁头会改变盘片表面的磁性,以存储数据;
  • 主轴带有马达,使盘片高速旋转;这种旋转允许磁头访问盘片上的任意位置;
  • 盘片数据存储的介质,通常有多个盘片叠加在一起,每个盘片有两个盘面;
  • 盘面盘片的每一面,用于存储数据;每个盘面都配备有对应的磁头;
  • 磁道盘面上的同心圆,数据以磁道为单位进行组织;每个磁道可以进一步被分割成多个扇区;
  • 柱面由所有盘面上相同磁道号的磁道组成;设想如果磁盘的多个盘片沿主轴方向穿透,形成的圆柱形结构即为柱面;
  • 扇区磁道进一步划分的单位,是磁盘存储数据的最小物理单元;每个扇区通常存储512字节或更多的数据;


磁盘抽象化

请添加图片描述

磁带设备的存储最终数据将被存储到磁带上;

将磁带拉直可以将其看成连续不断的空间;

将磁盘盘面进行抽象化;

由于盘片的高速转动+磁头臂带动磁头的移动可以将其看成是一个螺旋的运动;

螺旋化后拉直为一个以扇区为最小单位的连续不断的空间;

与磁带不同,磁带的访问为顺序访问,磁盘的访问通过盘片的高速旋转+磁头臂带动磁头可以进行随机访问;


磁盘的寻址方式

请添加图片描述

  • CHS(Cylinder-Head-Sector)寻址

    CHS寻址方式使用柱面号,磁头号和扇区号来定位磁盘上的数据;

    每个地址由三个部分组成:柱面号( C ),磁头号( H )和扇区号( S );

    • 柱面号( C ):表示磁盘上的柱面,柱面是所有盘片上相同半径的磁道的集合(从0开始编号);
    • 磁头号( H ):表示盘片上的磁头,磁头用于读取和写入数据(从0开始编号);
    • 扇区号( S ):表示磁道上的扇区,扇区是数据存储的最小单位(从1开始编号);
  • LBA(Logical Block Addressing)寻址

    LBA寻址方式使用逻辑块地址定位磁盘上的数据;

    每个逻辑块地址是一个唯一的整数,表示磁盘上的一个逻辑块(这里指扇区);

    • 逻辑块地址(LBA):表示磁盘上的一个逻辑块,逻辑块是数据存储的基本单位;
    • LBA寻址方式将整个磁盘视为一个连续的逻辑块数组,每个逻辑块都有一个唯一的地址;

因物理原因, LBA(Logical Block Addressing) 寻址最终以算法转换为 CHS(Cylinder-Head-Sector) 寻址;

  • LBA寻址转换为CHS寻址公式

    • 柱面号( C )

      C = LBA / ( H * S );
      
    • 磁头号( H )

      H = ( LBA / S ) % H;
      
    • 扇区号( S )

      S = ( LBA % S ) + 1; #CHS中扇区号从1开始计算
      
    • 例:

      • 磁盘有3盘片(6盘面);
      • 每个盘面有20000个扇区;
      • 每个盘面有50个磁道;
      • 每个磁道有400个扇区;
      • 需要访问的扇区编号(LBA)为28888;

      柱面号的计算:28888 / ( 50 * 400 ) = 1;

      相对扇区计算:28888 % ( 50 * 400 ) = 8888;

      磁道编号计算:8888 / 400 = 22;

      磁道上相对扇区编号:8888 % 400 = 88;

      结果:

      • 盘面编号:1;
      • 磁道编号:22;
      • 扇区编号:89(从1开始编号);

CHS寻址方式逆向得到LBA编号;


磁盘控制器

请添加图片描述

磁盘硬件中存在磁盘控制器(物理硬件);

  • 磁盘控制器的功能:
    • 数据传输
    • 执行命令
    • 错误检测和纠正
    • 缓存管理

在磁盘控制器中存在一系列的寄存器(硬件):命令寄存器,数据寄存器,状态寄存器,地址寄存器等;

  • 命令寄存器

    用于存储来自计算机的操作命令;

  • 数据寄存器

    用于暂存从计算机写入磁盘的数据,或者从磁盘读取的数据;

  • 状态寄存器

    用于指示当前磁盘的状态,以及最近一次操作的结果;

  • 地址寄存器

    用于指定数据在磁盘上的位置,如扇区号或磁道号;

同时在磁盘硬件中为用户提供了一系列的 接口/串口 ;

接口/串口 作为驱动程序与磁盘控制器之间的桥梁,操作系统将通过磁盘接口(接口/串口)间接调用磁盘控制器从而操作磁盘;


磁盘数据传输

请添加图片描述

计算机中存在一种允许外部设备直接与系统内存进行数据传输的硬件组件DMA;

DMA(Direct Memory Access,直接内存访问);

DMA在进行数据传输时无需经过CPU因此可以减少CPU负担;

假设用户请求创建一个名为newfile.txt新文件并写入数据:

  • 用户请求

    用户请求或进程调用open("newfile.txt", O_CREAT | O_WRONLY);

  • 系统调用处理

    CPU将系统调用转发给OS;

    OS内核文件系统模块接收请求;

  • 文件系统操作

    文件系统将维护对应的结构体并为其分配数据块;

  • 数据写入

    用户或应用程序调用write进行数据写入;

    文件系统写入分配的数据块;

  • DMA配置

    OS配置DMA控制器设置源地址和目标地址并启动DMA传输;

  • 驱动程序操作

    驱动程序将写入命令和数据块地址发送给磁盘控制器;

    驱动程序处理终端和状态信息;

  • 磁盘控制器操作

    磁盘控制器接收写入命令并解析;

    磁盘控制器通过DMA从内存读取数据并写入磁盘扇区;

    磁盘控制器报告状态;


文件系统

请添加图片描述

操作系统通常无法直接管理巨大的磁盘空间,因此需要对磁盘进行分区(类比Windows中对C,D,E,F盘的分区);

分区的方式一般通过操作系统维护其struct结构体即可;

struct partion{
    int start;
    int end;
    //...
}

而实际每个分区的大小也会比较大,需要继续对分区进行区分管理;

一般情况下每个分区都由以下组成:

  • 启动块(Boot Block)

    一般情况下BootBlock为引导块,只存在于系统盘当中;

    但有些情况也会存在其他分区当中;

    存在该块的分区一般被称为引导分区;

  • 块组(Block group)

    每个分区由若干个块组组成,由Blockgroup 0Blockgroup n;

    块组是文件系统重最基本的管理单元,每个块组包含多个数据块,Inode表和其他元数据;

(该图为演示,与内核空间映像存在差异)

每个分区中都存在一个属于自己的文件系统;

而块组中所包含的多个内容块即为文件系统的核心部分;

每个块组通常包含以下几个部分:

  • 超级块(Superblock)

    • 存储文件系统的全局元数据;
  • 块组描述符表(Group Descriptor Table)

    • 存储每个块组的描述符,描述块组的元数据位置和状态;
  • 块位图(Block Bitmap)

    • 跟踪块组中数据块的使用情况;
  • Inode位图(Inode Bitmap)

    • 跟踪块组中Inode的使用情况;
  • Inode表(Inode Table)

    • 存储文件和目录的元数据;
    • Inode表中将存若干个Inode;
    • Inode用于存储文件基本属性(如文件类型,文件权限,拥有者,所属组等);
  • 数据块(Data Blocks)

    • 存储实际的文件和目录内容;

假设存在一个文件系统,其应包含以下块组:

struct superblock {
    int fs_size;  // 文件系统的大小
    int block_size;  // 块大小
    int free_blocks;  // 空闲块数量
    int free_inodes;  // 空闲Inode数量
    // 其他文件系统元数据
};

struct group_descriptor {
    int block_bitmap;  // 块位图的起始位置
    int inode_bitmap;  // Inode位图的起始位置
    int inode_table;  // Inode表的起始位置
    int free_blocks;  // 块组中的空闲块数量
    int free_inodes;  // 块组中的空闲Inode数量
    // 其他块组元数据
};

struct block_group {
    struct superblock sb;  // 超级块
    struct group_descriptor gd;  // 块组描述符
    char block_bitmap[128];  // 块位图
    char inode_bitmap[128];  // Inode位图
    struct inode inodes[1024];  // Inode表
    char data_blocks[4096][4096];  // 数据块
};

struct inode {
    int file_type;  // 文件类型
    int permissions;  // 文件权限
    int size;  // 文件大小
    int block_pointers[12];  // 数据块指针
    // 其他文件元数据
};

磁盘中最小的存储单位为扇区,而在文件系统中最小的单位为 文件系统块 ;

存储数据的数据块中存在若干个 文件系统块 ;

文件系统块的大小必须 >= 扇区大小,一般最常用为4kb;

  • 在数据的存储中,一般只有最后一个文件系统块会产生空间浪费:

    假设一个文件的的大小为5kb,将会为其分配两个文件系统块用于存储,其中第二个块为最后一个块将浪费3kb的空间;


Inode

请添加图片描述

在Linux中,文件的内容与文件的属性构成一个完整的文件;

其中文件的内容与文件的属性是分开管理的;

  • 文件的内容管理在数据块(Data Blocks)中
  • 文件的属性管理在Inode表中的Inode中;

Inode可以看作是一个结构体,其包含一个文件的所有属性:

struct inode {
    uint16_t i_mode;        // 文件类型和权限
    uint16_t i_uid;         // 文件所有者的用户ID
    uint32_t i_size;        // 文件大小(字节)
    uint32_t i_atime;       // 最后访问时间
    uint32_t i_ctime;       // 创建时间
    uint32_t i_mtime;       // 最后修改时间
    uint32_t i_dtime;       // 删除时间
    uint16_t i_gid;         // 文件所属组的组ID
    uint16_t i_links_count; // 链接计数
    uint32_t i_blocks;      // 文件占用的块数
    uint32_t i_flags;       // 文件标志
    uint32_t i_block[15];   // 数据块指针
    // 其他文件系统特定的信息
};

其中每个Inode都在对应的文件系统中存在一个唯一编号(不同文件系统之间的Inode编号可以重复);

在Linux当中可以采用ls -li获取其Inode编号;

$ ls -li
total 0
2360117 -rw-rw-r-- 1 _XXX _XXX 0 Jun  1 18:02 newfile1.c
2360116 -rw-rw-r-- 1 _XXX _XXX 0 Jun  1 18:00 newfile.c

其中在每个块组中都存在一个Inode位图(Inode Bitmap)来跟踪该文件系统中的Inode的使用情况(利用位图可以大大节省跟踪时所使用的空间);

当需要访问一个文件时文件系统将根据Inode中的编号查找文件的目录和数据块指针从而访问文件的实际数据;

  • 文件的文件名不存在于Inode之中,而是单独存放在目录的数据结构当中

    每个目录在文件系统当中被视为一个特殊的文件,其自身拥有一个Inode与数据块;

    目录中的数据块保存了文件名与对应Inode编号的映射关系,这些映射关系被称为目录条目;

    • 目录条目的结构演示

      struct directory_entry {
          uint32_t inode_number;  // Inode编号
          char file_name[256];    // 文件名
      };
      

      当通过文件名访问文件时文件系统将会执行以下步骤:

      假设文件路径为/home/user/file.txt;

      • 根目录(/)

        查找根目录的Inode(通常为Inode2);

        在根目录的数据块中查找home目录的目录条并获取其Inode编号;

      • home目录

        查找home目录的Inode;

        home目录的数据块中,查找user目录的目录条目并获取其Inode编号;

      • user目录

        查找user目录的Inode;

        user目录的数据块中,查找file.txt的目录条目并获取其Inode编号;

      • file.txt文件

        查找file.txt的Inode;

        通过Inode中的数据块指针访问其文件的实际数据;


数据块(Data Blocks)

请添加图片描述

数据块中存在若干个文件系统块;

在文件系统中的块位图(Bolck Bitmap)即为跟踪当前文件系统中数据块内各个文件系统块的使用情况;

在Inode中存在一组数据块指针数组,一般情况这个指针数组的大小为15;

这个指针数组中包含了三种数据块指针:

  • 直接指针(Direct Pointers)
    • 通常有12个直接指针,每个直接指针直接指向一个数据块(下标0-11)。
    • 直接指针用于存储文件的前12个数据块。
  • 单重间接指针(Single Indirect Pointer)
    • 指向一个间接块,间接块中包含指向数据块的指针(下标12)。
    • 单重间接指针用于存储更多的数据块。
  • 双重间接指针(Double Indirect Pointer)
    • 指向一个双重间接块,双重间接块中包含指向间接块的指针,间接块中再包含指向数据块的指针(下标13)。
    • 双重间接指针用于存储更多的数据块。
  • 三重间接指针(Triple Indirect Pointer)
    • 指向一个三重间接块,三重间接块中包含指向双重间接块的指针,双重间接块中再包含指向间接块的指针,间接块中再包含指向数据块的指针(下标14)。
    • 三重间接指针用于存储更多的数据块。

可以将其中的数据块指针数据看成是一种树状结构;

删除一个文件的本质是删除其对应文件系统中的Block Bitmap以及相关映射删除(所以理论上数据的删除可以被修复),当需要再此使用时只需要对文件系统块对应的内容进行覆盖即可;


超级块(SuperBlock)

请添加图片描述

超级块中包含了当前分区中所有文件系统中的全局信息和元数据(文件系统大小,块大小,空闲块数量等);

通常超级块位于文件系统的起始位置,每个块组中都可以存在一个超级块以便于在超级块损坏时可以进行对超级块的恢复;

超级块通常存在以下内容:

  • 文件系统元数据
    • 超级块包含文件系统的全局元数据,如文件系统的大小、块大小、空闲块数量、空闲Inode数量等。
    • 这些元数据用于文件系统的管理和操作。
  • 文件系统状态
    • 超级块包含文件系统的状态信息,如文件系统是否已挂载、是否已清除等。
    • 这些状态信息用于文件系统的维护和恢复。
  • 文件系统配置
    • 超级块包含文件系统的配置参数,如块大小、Inode大小、块组大小等。
    • 这些配置参数用于文件系统的初始化和操作。

超级块的结构示例如下:

struct superblock {
    uint32_t s_inodes_count;        // 文件系统中的Inode总数
    uint32_t s_blocks_count;        // 文件系统中的块总数
    uint32_t s_r_blocks_count;      // 保留块总数
    uint32_t s_free_blocks_count;   // 空闲块总数
    uint32_t s_free_inodes_count;   // 空闲Inode总数
    uint32_t s_first_data_block;    // 第一个数据块的块号
    uint32_t s_log_block_size;      // 块大小(以2的幂次表示)
    uint32_t s_log_frag_size;       // 碎片大小(以2的幂次表示)
    uint32_t s_blocks_per_group;    // 每个块组的块数
    uint32_t s_frags_per_group;     // 每个块组的碎片数
    uint32_t s_inodes_per_group;    // 每个块组的Inode数
	//...
    // 其他文件系统特定的信息
};

文件系统开发者在超级块当中定义了一个魔数(硬编码)用于验证作用;

这个魔数由于硬编码同时并被文件系统保护的情况下并不容易偶然被更改;

当该魔数被更改表示文件系统出现错误,此时需要利用其他块组中的超级块(超级块的备份)对原先超级块进行修复以保证整体的高效性和安全性;


块组描述符(Group Descriptor)

请添加图片描述

块组描述符用于描述每个块组的元数据位置和状态;

其一般包含了所有块组的描述符,其中每个块组都有一个对应的块组描述符;

  • 块组描述符的作用

    • 描述块组元数据的位置

      块组描述符一般包含组内重要的元数据(块位图,Inode位图,Inode表)的起始位置;

      通过快描述符使得文件系统可以快速定位组内元数据;

    • 跟踪块组的状态

      块组描述符包含块组内空闲块和空闲Inode的数量;

      这些信息一般用于文件系统的管理和优化;

块组描述符的结构示例:

struct group_descriptor {
    uint32_t block_bitmap;  // 块位图的起始位置
    uint32_t inode_bitmap;  // Inode位图的起始位置
    uint32_t inode_table;   // Inode表的起始位置
    uint16_t free_blocks_count; // 块组中的空闲块数量
    uint16_t free_inodes_count; // 块组中的空闲Inode数量
    uint16_t used_dirs_count;   // 块组中的已使用目录数量
    // 其他块组元数据
};

格式化

当对分区进行格式化以创建或重新创建文件系统时不会删除具体块组中的数据块;

只需对其对应的属性进行重置即可;

当对分区进行格式化应进行以下操作:

  • 超级块(Super Block)
    格式化中每个块组的SuperBlock都会被重置以反映新的文件系统结构;

  • 块位图(Block Bitmap)
    格式化时所有的位图都会被重置,所有块最初都会标记为空闲;

  • Inode位图(Inode Bitmap)
    格式化时所有的Inode位图会被重置,所有的Inode最初都会标记为未使用;

  • 块组描述符(Block Group Descriptor)
    格式化中描述符会被更新一直想新初始化的位图和Inode表;

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

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

相关文章

WowTab:简洁界面,效率神器,重塑新标签页浏览体验

目录 一、插件简介 二、插件功能 2.1 支持两种模式切换 2.2 效率组件介绍 2.3 设置中心 三、使用总结 四、插件安装说明 4.1 Edge安装 4.2 Chrome安装 大家在日常办公当中,是不是觉得新标签页太平淡无奇?来,给自己的浏览器加点料&…

uniapp学习(001 前期介绍)

零基础入门uniapp Vue3组合式API版本到咸虾米壁纸项目实战,开发打包微信小程序、抖音小程序、H5、安卓APP客户端等 总时长 23:40:00 共116P 此文章包含第1p-第p10的内容 简介 目录结构 效果 打包成小程序 配置开发者工具 打开安全按钮 使用uniapp的内置组件…

计算机网络学习实践:模拟PPP协议验证虚拟局域网(VLAN)

计算机网络实践:模拟PPP协议&&验证虚拟局域网(VLAN) 挺有意思的大家可以跟着做一做,我是跟着韩志刚老师的视频做的 https://www.bilibili.com/video/BV1Qr4y1N7cH?p31&vd_source7831c5b97cfc5c745eb48ff04f6515e7 …

PostgreSQL 17 Beta1 发布,酷克数据再次贡献核心力量

得益于全球的开发者贡献,PostgreSQL已成长为一款拥有众多全球用户和贡献者、成熟稳定的开源数据库。2024年5月23日,PostgreSQL全球开发组宣布,PostgreSQL 17的首个 Beta 版本现已开放下载。本次新版本带来了众多惊喜。值得一提的是&#xff0…

算法金 | 再见,PCA 主成分分析!

1. 概念:数据降维的数学方法 定义 主成分分析(PCA)是一种统计方法,通过正交变换将一组可能相关的变量转换为一组线性不相关的变量,这组新的变量称为主成分。大白话,PCA能够从数据中提取出最重要的特征&am…

深入JVM:全面解析GC调优

文章目录 深入JVM:全面解析GC调优一、序言二、GC调优指标三、GC在线监控1、Jstat工具2、VisualVM工具 四、GC日志分析1、收集GC日志2、GCViewer工具3、GCeasy工具 五、GC问题调优1、调整JVM内存大小(1)调整堆内存大小及比例(2&…

从头搭hadoop集群--分布式hadoop集群搭建

模板虚拟机安装配置见博文:https://blog.csdn.net/weixin_66158110/article/details/139236148 配置文件信息如下:https://pan.baidu.com/s/1074eD5aNVugEPcjwVvi9jA?pwdl1xq(提取码:l1xq) hadoop版本:h…

行车记录仪人体感应雷达开关模块,飞睿智能雷达模块穿透玻璃、告别漏触烦恼,安防停车监控新方案

随着汽车保有量的持续增长,行车记录仪作为汽车安全配件的必备品,其重要性日益凸显。然而,传统的行车记录仪传感器在停车时往往存在无法穿透玻璃、漏触等问题,给车主带来了诸多不便和安全隐患。本文将深入探讨停车场景下&#xff0…

2024年清洁能源与可持续发展国际会议(ICDESMF 2024)

2024 International Conference on Clean Energy and Sustainable Development 【1】大会信息 会议简称:ICDESMF 2024 大会时间:2024-07-22 大会地点:中国大理 截稿时间:2024-07-08(以官网为准) 审稿通知&#xff1a…

【云原生】Kubernetes----POD控制器

目录 引言 一、Pod控制器概述 二、Pod控制器的种类 (一)ReplicaSet (二)Deployment (三)StatefulSet (四)DaemonSet (五)Job 三、使用POD控制器 &a…

五、nodejs存储图片

nodejs存储图片 // 静态托管和数据库创建 创建数据库 新建Public进行静态托管 新建个img的文件夹 在index.js里 // 托管静态 app.use(/public, express.static(./Public))//托管静态资源 /*** 1.引入一个express框架* 2.在加载所有服务模块前,要先连接数据库* …

MySQL是怎么保证持久性的(redo log日志相关)

Mysql中 事务的很多实现,都是因为有日志的支撑,比如binlog、undo log、redo log等 MySQL是怎么保证持久性的 持久性是指,事务一旦提交,它对数据库的改变就应该是永久性的,接下来的其他操作或故障不能对其有影响。In…

Java(十)——内部类

文章目录 内部类静态内部类实例内部类匿名内部类局部内部类 内部类 Java内部类是一种特殊的类定义方式,它允许在一个类的内部定义另一个类。 内部类可以访问其所在外部类的成员变量和成员方法,这使得它非常适用于封装与外部类紧密相关的私有逻辑。 内…

如何处理SSL证书过期问题?

SSL证书是网络安全的重要组成部分,它为网站提供了数据加密、身份验证和增强用户信任等多重保护。然而,SSL证书并非永久有效,其有效期通常为一年。当SSL证书过期时,网站安全性会受到影响,甚至可能面临安全风险。本文旨在…

实战|基于YOLOv10与MobileSAM实现目标检测与分割【附完整源码】

《博主简介》 小伙伴们好,我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源,可关注公-仲-hao:【阿旭算法与机器学习】,共同学习交流~ 👍感谢小伙伴们点赞、关注! 《------往期经典推…

开发一个comfyui的自定义节点-支持输入中文prompt

文章目录 目标功能开发环境实现过程翻译中文CLIP编码拓展仓库地址完整代码目标功能 目前comfyui的prompt提示词输入节点 CLIP Text Encode 只支持输入英文的prompt,而有时候我们需要自己制定一些prompt,所以就得将我们想要的提示词翻译为英文后再复制粘贴到该节点的输入框中…

PMP考试难吗?考试通过率有多少?

我们通常以考试的通过率来评判一个考试的难易程度。通常通过率达到60%以上,这个考试就不太难;达到80% ,这个考试就是不难的。 PMP考试难吗? 不少想要考PMP的小伙伴都会有这样的疑惑,首先以PMP的含金量来说&#xff0…

基于Dify的QA数据集构建(附代码)

大模型相关目录 大模型,包括部署微调prompt/Agent应用开发、知识库增强、数据库增强、知识图谱增强、自然语言处理、多模态等大模型应用开发内容 从0起步,扬帆起航。 大模型应用向开发路径:AI代理工作流大模型应用开发实用开源项目汇总大模…

python 内置map()函数(高效处理序列数据方法,将函数应用于一个序列的每个元素)(懒加载)

文章目录 深入解析 Python 内置函数 map()函数定义与用法基本示例 map() 与列表推导式比较(列表推导式在语法上更加简洁, map() 在某些情况下执行效率更高)示例:将数字转化为字符串 map() 结合 lambda 函数使用多个序列结论 深入解…

kubernetes负载均衡---MetalLB

https://github.com/metallb/metallb 参考 : https://mp.weixin.qq.com/s/MBOWfcTjFMmgJFWw-FIk0Q 自建的Kubernetes集群,默认情况下是不支持负载均衡的。当需要提供服务的外部访问时,可使用 Ingress、NodePort等方式。他们都存在一些问题 …