SDN实验报告

news/2024/11/8 14:43:57/文章来源:https://www.cnblogs.com/magges/p/18517989

SDN上机实验

实验目的

    1. 能够使用Mininet的实现网络拓扑构建;
    1. 熟悉Open vSwitch交换机的基本配置;
    1. 熟悉OpenFlow协议的通信原理
    1. 掌握pox控制器的基本使用方法;
    1. 掌握Ryu控制期的基本使用方法;
    1. 掌握北向应用的基本开发方法

实验环境

基础环境选择ubuntu-20.04.6-desktop-amd64

实验内容

  • 任务1
      1. 展示构建拓扑所用到的Python代码和Mininet命令,以及VLAN配置后的主机连通性测试结果;
      1. 写出四个问题的答案;
  • 任务2
      1. POX关键代码、交换机下发的流表项以及连通性测试结果;
      1. RYU关键源码、交换机下发的流表项以及连通性测试结果;
  • 任务3
      1. 程序源代码和相关配置文件内容直接粘贴至报告中
      1. 截图展示运行结果
  • 任务4
      1. 提交完整系统设计的图文说明,粘贴关键代码和相关配置文件内容,每段代码不超过一页,截图展
        示最终的系统运行结果;
      1. 系统源代码项目文件,ZIP格式,作为报告附件;
      1. 系统的视频介绍,MP4格式,编码需为H264,5分钟以内,大小不超过500M,作为报告附件。

实验操作

任务一







  • 熟悉OpenFlow协议,对照OpenFlow源码,了解OpenFlow主要消息类型对应的数据结构定义,回答以下问题:

    • 交换机与控制器建立通信时是使用TCP协议还是UDP协议?

    交换机与控制器之间建立的通信是使用 TCP 协议。

    • OpenFlow控制器默认用哪个端口号和交换机通信?

    66333端口

    • Packet_IN消息由通信的哪一端发出,产生该消息的原因有哪些?

    Packet-in消息由OpenFlow交换机发出并发送到OpenFlow控制器
    产生Packet_IN 消息的原因包括:
    \(~~~\)交换机收到一个数据包后,会查找流表,如果流表中没有匹配条目,则交换机会将数据包封装在Packet-in消息中发送给控制器处理.
    \(~~~\)数据包被标记为需要被转发到控制器的类型.
    \(~~~\)控制器或交换机的流表不是最新的,或者没有针对特定数据包的匹配项.

    • Flow_Mod和Packet_OUT的区别是什么

\(~~~\)Flow_Mod:这是一种用于修改流表项的消息。它可以用于添加、修改或删除流表中的流项。通常由控制器发出,以期望控制和管理数据包流转的方式。Flow_Mod 还可以包含与流匹配条件相关的动作,例如转发到特定端口、丢弃等。
\(~~~\)Packet_OUT:这是一种用于直接在交换机的数据平面上执行操作的消息。Packet_OUT 消息允许控制器向交换机发送一个具体的数据包并指定如何处理该数据包(例如,发送到特定端口)。主要用于实现更复杂的流量管理和即时响应。

topo_ovsvlan.py

from mininet.topo import Topo
from mininet.net import Mininet
from mininet.node import OVSSwitch, Controller, RemoteController, Host
from mininet.cli import CLI
from mininet.log import setLogLevel, info
from mininet.link import TCLinkclass MyTopo(Topo):"Simple topology example."def build(self):# Create switchess1 = self.addSwitch('s1', dpid='00:00:00:00:00:00:00:01',protocols='OpenFlow13')s2 = self.addSwitch('s2', dpid='00:00:00:00:00:00:00:02',protocols='OpenFlow13')# Create hostsh1 = self.addHost('h1', ip='192.168.0.101/24')h2 = self.addHost('h2', ip='192.168.0.102/24')h3 = self.addHost('h3', ip='192.168.0.103/24')h4 = self.addHost('h4', ip='192.168.0.104/24')h5 = self.addHost('h5', ip='192.168.0.105/24')h6 = self.addHost('h6', ip='192.168.0.106/24')# Add links with specific bandwidth and delay settingslinkopts0 = dict(bw=300, delay='1ms', loss=0)linkopts1 = dict(bw=100, delay='1ms', loss=0)self.addLink(h1, s1, cls=TCLink, **linkopts1)self.addLink(h2, s1, cls=TCLink, **linkopts1)self.addLink(h3, s1, cls=TCLink, **linkopts1)self.addLink(h4, s2, cls=TCLink, **linkopts1)self.addLink(h5, s2, cls=TCLink, **linkopts1)self.addLink(h6, s2, cls=TCLink, **linkopts1)self.addLink(s1, s2, cls=TCLink, **linkopts0)
def startMyNet():"Create network and run CLI"topo = MyTopo()net = Mininet(topo=topo, switch=OVSSwitch, controller=None,autoSetMacs=True, autoStaticArp=True)c0 = net.addController('c0', controller=Controller)net.start()# Add OpenFlow rules to the switchess1 = net.get('s1')s2 = net.get('s2')# Rules for s1s1.cmd('ovs-ofctl -O OpenFlow13 del-flows s1')s1.cmd('ovs-ofctl -O OpenFlow13 add-flow s1 priority=100,in_port=1,actions=push_vlan:0x8100,set_field:4096-\>vlan_vid,output:4')s1.cmd('ovs-ofctl -O OpenFlow13 add-flow s1 priority=100,in_port=2,actions=push_vlan:0x8100,set_field:4097-\>vlan_vid,output:4')s1.cmd('ovs-ofctl -O OpenFlow13 add-flow s1 priority=100,in_port=3,actions=push_vlan:0x8100,set_field:4098-\>vlan_vid,output:4')s1.cmd('ovs-ofctl -O OpenFlow13 add-flow s1 priority=100,dl_vlan=0,actions=pop_vlan,output:1')s1.cmd('ovs-ofctl -O OpenFlow13 add-flow s1 priority=100,dl_vlan=1,actions=pop_vlan,output:2')s1.cmd('ovs-ofctl -O OpenFlow13 add-flow s1 priority=100,dl_vlan=2,actions=pop_vlan,output:3')# Rules for s2s2.cmd('ovs-ofctl -O OpenFlow13 del-flows s2')s2.cmd('ovs-ofctl -O OpenFlow13 add-flow s2 priority=100,in_port=1,actions=push_vlan:0x8100,set_field:4096-\>vlan_vid,output:4')s2.cmd('ovs-ofctl -O OpenFlow13 add-flow s2 priority=100,in_port=2,actions=push_vlan:0x8100,set_field:4097-\>vlan_vid,output:4')s2.cmd('ovs-ofctl -O OpenFlow13 add-flow s2 priority=100,in_port=3,actions=push_vlan:0x8100,set_field:4098-\>vlan_vid,output:4')s2.cmd('ovs-ofctl -O OpenFlow13 add-flow s2 priority=100,dl_vlan=0,actions=pop_vlan,output:1')s2.cmd('ovs-ofctl -O OpenFlow13 add-flow s2 priority=100,dl_vlan=1,actions=pop_vlan,output:2')s2.cmd('ovs-ofctl -O OpenFlow13 add-flow s2 priority=100,dl_vlan=2,actions=pop_vlan,output:3')CLI(net)net.stop()
if __name__ == '__main__':setLogLevel('info')startMyNet()
遇到困难和解决方法

\(~~\)在使用Mininet创建拓扑和配置VLAN时,最初我在配置交换机和主机的VLAN ID时出现了错误。这导致主机之间的连通性未能如预期实现。例如,h1与h4之间无法互通。为了解决这个问题,我认真查阅了Mininet和Open vSwitch的相关文档,确保在使用ovs-vsctl命令时正确指定VLAN ID。同时,我借助ovs-ofctl show命令检查了交换机的配置。经过几次尝试后,我最终成功配置了VLAN,确保了 h1 - h4、h2 - h5 和 h3 - h6 的互通性,解决了连通性的问题。

个人感想和总结

\(~~\)这一任务让我对Mininet和Open vSwitch的操作有了更深入的理解。在实际配置VLAN时,我体会到了网络配置的重要性以及对细节的关注。做好每一步配置不仅是实现网络互通的基础,也是后续任务顺利展开的保障。这次实验强化了我的实践能力,我期待在在后续的学习中进一步掌握复杂的网络环境配置。

任务二

连接pox控制器





由于pox仅支持openflow 1.0,所以需要用到另一个openflow10拓扑

from mininet.topo import Topo
from mininet.net import Mininet
from mininet.node import OVSSwitch, Controller, RemoteController, Host
from mininet.cli import CLI
from mininet.log import setLogLevel, info
from mininet.link import TCLinkclass MyTopo(Topo):"Simple topology example."def build(self):# Create switchess1 = self.addSwitch('s1', dpid='00:00:00:00:00:00:00:01',protocols='OpenFlow10')
s2 = self.addSwitch('s2', dpid='00:00:00:00:00:00:00:02',protocols='OpenFlow10')# Create hostsh1 = self.addHost('h1', ip='192.168.0.101/24')h2 = self.addHost('h2', ip='192.168.0.102/24')h3 = self.addHost('h3', ip='192.168.0.103/24')h4 = self.addHost('h4', ip='192.168.0.104/24')h5 = self.addHost('h5', ip='192.168.0.105/24')h6 = self.addHost('h6', ip='192.168.0.106/24')# Add links with specific bandwidth and delay settingslinkopts0 = dict(bw=300, delay='1ms', loss=0)linkopts1 = dict(bw=100, delay='1ms', loss=0)self.addLink(h1, s1, cls=TCLink, **linkopts1)self.addLink(h2, s1, cls=TCLink, **linkopts1)self.addLink(h3, s1, cls=TCLink, **linkopts1)self.addLink(h4, s2, cls=TCLink, **linkopts1)self.addLink(h5, s2, cls=TCLink, **linkopts1)self.addLink(h6, s2, cls=TCLink, **linkopts1)self.addLink(s1, s2, cls=TCLink, **linkopts0)def startMyNet():"Create network and run CLI"topo = MyTopo()net = Mininet(topo=topo, switch=OVSSwitch, controller=None,autoSetMacs=True, autoStaticArp=True)# Add a remote controllerc0 = net.addController('c0', controller=RemoteController, ip='127.0.0.1',port=6633)net.start()# Add OpenFlow rules to the switches and any other configurations neededCLI(net)net.stop()if __name__ == '__main__':
setLogLevel('info')

poxcontroller.py

from pox.core import core
import pox.openflow.libopenflow_01 as of
from pox.lib.util import dpidToStrlog = core.getLogger()def _handle_ConnectionUp(event):dpid = event.connection.dpidsw = dpidToStr(dpid)log.info("Switch %s has connected.", sw)if sw == '00-00-00-00-00-01':  # s1configure_switch(event.connection, is_s1=True)elif sw == '00-00-00-00-00-02':  # s2configure_switch(event.connection, is_s1=False)else:log.warning("Unknown switch connected: %s", sw)def configure_switch(conn, is_s1):if is_s1:host_pairs = [("192.168.0.101", "192.168.0.104", 1),("192.168.0.102", "192.168.0.105", 2),("192.168.0.103", "192.168.0.106", 3)]else:host_pairs = [("192.168.0.104", "192.168.0.101", 1),("192.168.0.105", "192.168.0.102", 2),("192.168.0.106", "192.168.0.103", 3)]for src_ip, dst_ip, local_port in host_pairs:# 配置从源 IP 到目标 IP 的流表项msg = of.ofp_flow_mod()msg.priority = 1000msg.match.dl_type = 0x0800  # IPv4msg.match.nw_src = src_ipmsg.match.nw_dst = dst_ipmsg.actions.append(of.ofp_action_output(port=local_port))  # 使用local_port  try:conn.send(msg)log.info("Flow mod sent: src_ip=%s, dst_ip=%s, action_port=%d", src_ip, dst_ip, local_port)  except Exception as e:log.error("Failed to send flow mod: %s", e)# 配置从目标 IP 到源 IP 的流表项msg = of.ofp_flow_mod()msg.priority = 1000msg.match.dl_type = 0x0800  # IPv4msg.match.nw_src = dst_ipmsg.match.nw_dst = src_ipmsg.actions.append(of.ofp_action_output(port=local_port))  # 使用local_port  try:conn.send(msg)log.info("Flow mod sent: src_ip=%s, dst_ip=%s, action_port=%d", dst_ip, src_ip, local_port)  except Exception as e:log.error("Failed to send flow mod: %s", e)log.info("Configured IP-based flows for switch %s", "s1" if is_s1 else "s2")  def launch():core.openflow.addListenerByName("ConnectionUp", _handle_ConnectionUp)log.info("POX IP-based communication module running.")

连接ryu控制器






ryucontroller.py

from ryu.base import app_manager  
from ryu.controller import ofp_event  
from ryu.controller.handler import MAIN_DISPATCHER, set_ev_cls  
from ryu.ofproto import ofproto_v1_0  
from ryu.lib.packet import packet, ethernet, ipv4  class CustomSwitch(app_manager.RyuApp):  OFP_VERSIONS = [ofproto_v1_0.OFP_VERSION]  def __init__(self, *args, **kwargs):  super(CustomSwitch, self).__init__(*args, **kwargs)  @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)  def _packet_in_handler(self, ev):  msg = ev.msg  datapath = msg.datapath  ofproto = datapath.ofproto  parser = datapath.ofproto_parser  dpid = datapath.id  self.logger.info("Packet in DPID: %s", dpid)  # 打印 DPID  pkt = packet.Packet(msg.data)  eth = pkt.get_protocol(ethernet.ethernet)  ip_pkt = pkt.get_protocol(ipv4.ipv4)  if ip_pkt is None:  return  src_ip = ip_pkt.src  dst_ip = ip_pkt.dst  self.logger.info("Received packet: SRC IP: %s, DST IP: %s", src_ip, dst_ip)  # 根据源 IP 和目的 IP 来决定流表规则  out_port = None  if dpid == 1:  # Switch 1  if src_ip == '192.168.0.101' and dst_ip == '192.168.0.104':  # h1 -> h4  out_port = 4  elif src_ip == '192.168.0.104' and dst_ip == '192.168.0.101':  # h4 -> h1  out_port = 1  elif src_ip == '192.168.0.102' and dst_ip == '192.168.0.105':  # h2 -> h5  out_port = 4  elif src_ip == '192.168.0.105' and dst_ip == '192.168.0.102':  # h5 -> h2  out_port = 2  elif src_ip == '192.168.0.103' and dst_ip == '192.168.0.106':  # h3 -> h6  out_port = 4  elif src_ip == '192.168.0.106' and dst_ip == '192.168.0.103':  # h6 -> h3  out_port = 3  elif dpid == 2:  # Switch 2  if src_ip == '192.168.0.101' and dst_ip == '192.168.0.104':  # h1 -> h4  out_port = 1  elif src_ip == '192.168.0.104' and dst_ip == '192.168.0.101':  # h4 -> h1  out_port = 4  elif src_ip == '192.168.0.102' and dst_ip == '192.168.0.105':  # h2 -> h5  out_port = 2  elif src_ip == '192.168.0.105' and dst_ip == '192.168.0.102':  # h5 -> h2  out_port = 4  elif src_ip == '192.168.0.103' and dst_ip == '192.168.0.106':  # h3 -> h6  out_port = 3  elif src_ip == '192.168.0.106' and dst_ip == '192.168.0.103':  # h6 -> h3  out_port = 4  if out_port is not None:  self.logger.info("Installing flow: DPID %s, SRC IP %s -> DST IP %s, OUTPORT %s", dpid, src_ip, dst_ip, out_port)  actions = [parser.OFPActionOutput(out_port)]  # 安装流表规则  match = parser.OFPMatch(dl_type=eth.ethertype, nw_src=src_ip, nw_dst=dst_ip)  mod = parser.OFPFlowMod(datapath=datapath, priority=1, match=match, actions=actions)  datapath.send_msg(mod)  # 立即转发数据包  out = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id, in_port=msg.in_port, actions=actions, data=msg.data)  datapath.send_msg(out)  else:  self.logger.info("No matching flow found for DPID %s, SRC IP %s, DST IP %s", dpid, src_ip, dst_ip)
遇到困难和解决方法

\(~~\)在连接POX控制器时,流表项未能有效下发给交换机,导致主机之间无法正常通信。通过查看POX控制器的调试信息,我发现流表的优先级设置不合理,导致流量未能正确匹配。我调整了流表项的优先级,并并确保了匹配条件的准确性,成功通过控制器下发正确的流表项,实现了h1 - h4、h2 - h5和h3 - h6之间的互通。在切换到Ryu控制器时,我也遇到了相似的问题,但通过其强大的API文档,我快速找到了问题的根源,并顺利实现了想要的功能。

个人感想和总结

\(~~\)对比POX和Ryu控制器的使用,我意识到不同控制器在流表管理上的差异。Ryu控制器功能更为强大,适合进行更复杂的操作。这个任务让我更加熟悉控制器的工作机制,以及如何通过编程实现网络流量的管理和转发。实践中对流表的深入理解,大大增强了我在网络编程上的自信。

任务三

查看拓扑信息,包括主机、
交换机和链路,以及显示每台交换机的所有流表项

getxinxi.py

from mininet.net import Mininet  
from mininet.node import RemoteController  
from mininet.cli import CLI  
from mininet.log import setLogLevel  
import os  def print_topology(MyTopo):  "Prints the topology of the network including hosts, switches, and links."  print("Network Topology:")  print("Hosts:")  for host in MyTopo.hosts:  print(f" - {host.name}: {host.IP()}")  print("Switches:")  for switch in MyTopo.switches:  print(f" - {switch.name}")  print("Links:")  for link in MyTopo.links:  print(f" - {link}")  def print_flow_tables(switches):  "Prints the flow tables for each switch."  for switch in switches:  print(f"\nFlow table for switch {switch.name}:")  flows = switch.cmd(f'ovs-ofctl dump-flows {switch.name}')  print(flows)  def add_flow(switch, match, actions):  "Adds a flow rule to the switch."  command = f'ovs-ofctl add-flow {switch.name} "{match}, {actions}"'  switch.cmd(command)  print(f"Added flow to {switch.name}: {match} -> {actions}")  def delete_flow(switch, match):  "Deletes a flow rule from the switch."  command = f'ovs-ofctl del-flow {switch.name} "{match}"'  switch.cmd(command)  print(f"Deleted flow from {switch.name}: {match}")  def main():  setLogLevel('info')  # Connect to running Mininet instance  MyTopo = Mininet(controller=RemoteController)  MyTopo.start()  # Print topology  print_topology(MyTopo)  # Print flow tables  print_flow_tables(MyTopo.switches)  # Example of adding and deleting flows  add_flow(MyTopo.get('s1'), 'in_port=1,dl_type=0x0800,nw_dst=192.168.0.102', 'actions=output:2')  delete_flow(MyTopo.get('s1'), 'in_port=1,dl_type=0x0800,nw_dst=192.168.0.102')  # CLI for user interaction  CLI(MyTopo)  MyTopo.stop()  if __name__ == '__main__':  main()
遇到困难和解决方法

\(~~\)在编写Ryu的北向应用程序时,我遇到的主要问题是如何获取和展示拓扑信息。最开始尝试从Ryu的API中获取信息时,发现信息结构复杂,难以快速理解。为此,我参考了Ryu的示例代码和社区资源,逐步梳理出获取主机、交换机和链路信息的逻辑。通过实践,我成功实现了拓扑的可视化,同时也能够动态添加和删除流表项。这个过程让我深刻理解了如何利用Ryu提供的API进行流表管理。

个人感想和总结

\(~~\)这一任务不仅让我掌握了如何通过编程获取网络拓扑信息和流表项,还让我体会到了软件定义网络的灵活性和强大能力。通过实际的应用开发,我更加深刻地认识到,北向应用在SDN架构中的重要性。未来,我希望能继续探索网络编程,并将这一技术应用于更多的实际场景,尤其是在网络安全和负载均衡领域的应用。整体来说,这次任务让我对SDN有了更全面的认识,对我的学习旅程产生了积极的推动。

任务四

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

双11买ToDesk远程控制&云电脑,看这一篇就够了!

今年双十一各大商家实在是太卷了,预售定金满减凑单一堆花活。但小编发现ToDesk远程控制&云电脑的双十一活动不一般。 囊括了远程控制各种会员版本的年包优惠,云电脑的计时机包时机活动,充值还送钱,优惠力度大,而且直接减钱,不费脑子就拿下超值价格。小编给大家简单整理了…

Java 面试用什么项目?全是商场秒杀 RPC,我吐了

看了几百份简历,真的超过 90% 的小伙伴的项目是商城、RPC、秒杀、论坛、外卖、点评等等烂大街的项目,人人都知道这些项目烂大街了,但大部分同学还是得硬着头皮做,没办法,网络上能找到的、教程比较完善的就这些项目了,做的话好歹有个项目,不做那就真能写学校做的垃圾学生…

极狐GitLab 签约某清洁能源高科技企业,助力零碳技术开创更加美好的零碳世界

客户背景 该客户是一家全球领先的清洁能源高科技公司,总部位于江苏省。公司自成立之初就致力于为全球客户提供清洁、高效、安全的能源解决方案,希望能用高科技技术让新能源发挥更大价值,让世界变得更加美好。当前,该客户在多个能源领域都有领先的产品和成熟的解决方案,也一…

DAC8568IAPWR 数据手册 具有 2.5V、2ppm/C 内部基准电压的 DAC7568、DAC8168、DAC8568 12/14/16 位、8 通道、超低毛刺、电压输出数模转换器芯片

DAC7568、DAC8168 和 DAC8568 分别为 12 位、14位和 16 位低功耗、电压输出、八通道数模转换器(DAC)。这些器件包括一个 2.5V、2ppm/C 内部基准电压(默认禁用),可提供 2.5V 或 5V 的满量程输出电压范围。内部基准电压初始精度为 0.004%,而且可在 VREFIN/VREFOUT 引脚上提供…

ue4资产序列化从入门到精通: 第一章 初识序列化

一、写作目的:(全文字数4926,阅读大约需25min) 首先,我有一个相关的需求要做,然后在拜读了网络上各大UE4序列化解析的文章后,发现大都讲的很模糊,对新入序列化大门的小白非常不友好。有的直接贴上一大段代码(好似直接糊脸上的不解释连招),也有的讲着讲着嘎然而止,也有的…

Hadoop及Spark环境配置与运行实例

本文章为Hadoop与Spark环境配置及Hadoop环境下使用mapreduce进行wordcount、Spark环境下使用KMeans进行鸢尾花数据集聚类实例运行实验记录。一、参考资料重要说明本文章为大数据分析课程实验之Hadoop与Spark平台配置记录及示例演示,其中Hadoop配置部分绝大多数内容源自参考资料…

salesforce零基础学习(一百四十一)刷新dev sandbox需要强制group

本篇参考:https://help.salesforce.com/s/articleView?id=sf.data_sandbox_selective_access.htm&type=5 背景:最近同事刷新sandbox发现点击create不生效,并且无任何提示(后续可能优化)。习惯了直接创建或者刷新的老司机们可能看不出来Sandbox Access标红提示来着,恰…

TPS26600PWPR 数据手册 一款集成反向输入极性保护的 工业电子保险丝芯片 浪涌保护器

TPS2660x 器件是一系列功能丰富的紧凑型高电压电子保险丝,具有一整套保护 功能)。4.2V 至 60V 的宽电源输入范围可实现对众多常用直流总线电压的控制。器件可以承受并保护由高达 60V 的正负电源供电的负载。集成的背靠背 FET 提供反向电流阻断功能,因此器件非常适合在电源故…

因为采购同行,造成的一次Java heap space 堆内存溢出

Caused by: java.sql.SQLException: Java heap space不多说了,没见过这样的。 报错原因是JVM内存XMX超了 Xms512m -Xmx2048m下班记得打卡

2个月搞定计算机二级C语言——真题(10)解析qg

合集 - 3个月搞定计算机二级C语言(6)1.2个月搞定计算机二级C语言——真题(5)解析10-292.2个月搞定计算机二级C语言——真题(6)解析10-303.2个月搞定计算机二级C语言——真题(7)解析11-034.2个月搞定计算机二级C语言——真题(8)解析11-035.2个月搞定计算机二级C语言——…

cmu15545-数据存储(Database Storage)

蓝图数据库自己管理磁盘数据和缓冲区,而不是通过操作系统管理(Os is not your friend.)。 三层视图 数据库以页(page)为存储数据的基本单位,文件(file)是一系列页的集合,页中存储页数据(data),形成文件-页-数据三层架构。 文件有不同的组织形式,页包含页头和页数据…