This repository has been archived on 2026-04-18. You can view files and clone it, but cannot push or open issues or pull requests.
Virtual-Memory-Demo/Assets/Scripts/Simulation/TranslatorEngine.cs

349 lines
13 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using VMdemo.Core;
namespace VMdemo.Simulation
{
public enum TranslationStep
{
GenerateVA = 0,
SplitVA = 1,
LookupTLB = 2,
LookupPageTable = 3,
HandlePageFault = 4,
ComposePA = 5,
Finalize = 6
}
public readonly struct AccessExecutionResult
{
public AccessExecutionResult(
int round,
ulong virtualAddress,
AddressParts addressParts,
ulong pfn,
ulong physicalAddress,
bool tlbHit,
bool pageTableHit,
bool pageFault,
EvictionInfo eviction)
{
Round = round;
VirtualAddress = virtualAddress;
AddressParts = addressParts;
Pfn = pfn;
PhysicalAddress = physicalAddress;
TlbHit = tlbHit;
PageTableHit = pageTableHit;
PageFault = pageFault;
Eviction = eviction;
}
public int Round { get; }
public ulong VirtualAddress { get; }
public AddressParts AddressParts { get; }
public ulong Pfn { get; }
public ulong PhysicalAddress { get; }
public bool TlbHit { get; }
public bool PageTableHit { get; }
public bool PageFault { get; }
public EvictionInfo Eviction { get; }
}
public class TranslatorEngine
{
private readonly SimulationConfig _config;
private readonly ConfigValidator.DerivedConfig _derivedConfig;
private readonly int? _seed;
private AddressGenerator _addressGenerator;
private TlbCache _tlbCache;
private TwoLevelPageTable _pageTable;
private PhysicalMemoryManager _physicalMemoryManager;
private bool _hasPendingForcedVirtualAddress;
private ulong _pendingForcedVirtualAddress;
private EvictionInfo _lastEviction;
public TranslatorEngine(SimulationConfig config, int? seed = null)
{
if (!ConfigValidator.TryValidateAndBuildDerived(config, out var derived, out var errorMessage))
{
throw new ArgumentException(errorMessage, nameof(config));
}
_config = config;
_derivedConfig = derived;
_seed = seed;
State = new SimulationState();
Stats = new StatsCollector();
AddressTranslatorUtils.GetPageTableBitLayout(derived.VpnBits, out var l1Bits, out var l2Bits);
L1Bits = l1Bits;
L2Bits = l2Bits;
_addressGenerator = CreateAddressGenerator();
_tlbCache = new TlbCache(_config.tlbEntries);
_pageTable = new TwoLevelPageTable(L1Bits, L2Bits);
_physicalMemoryManager = new PhysicalMemoryManager(_derivedConfig.FrameCount);
Reset();
}
public SimulationState State { get; }
public TranslationStep CurrentStep => State.CurrentStep;
public int MachineBits => _config.machineBits;
public int VaBits => _config.vaBits;
public int PageSizeKB => _config.pageSizeKB;
public int PhysicalMemoryMB => _config.physicalMemoryMB;
public int OffsetBits => _derivedConfig.OffsetBits;
public int L1Bits { get; }
public int L2Bits { get; }
public ulong MaxVirtualAddress => _addressGenerator.MaxVirtualAddress;
public TlbCache TlbCache => _tlbCache;
public TwoLevelPageTable PageTable => _pageTable;
public PhysicalMemoryManager PhysicalMemoryManager => _physicalMemoryManager;
public StatsCollector Stats { get; }
public EvictionInfo LastEviction => _lastEviction;
public void Reset()
{
State.Reset();
_lastEviction = EvictionInfo.None;
_hasPendingForcedVirtualAddress = false;
_pendingForcedVirtualAddress = 0UL;
Stats.Reset();
_addressGenerator = CreateAddressGenerator();
_tlbCache = new TlbCache(_config.tlbEntries);
_pageTable = new TwoLevelPageTable(L1Bits, L2Bits);
_physicalMemoryManager = new PhysicalMemoryManager(_derivedConfig.FrameCount);
}
public void SetNextVirtualAddress(ulong virtualAddress)
{
EnsureVirtualAddressInRange(virtualAddress);
_pendingForcedVirtualAddress = virtualAddress;
_hasPendingForcedVirtualAddress = true;
}
public bool StepOnce()
{
switch (State.CurrentStep)
{
case TranslationStep.GenerateVA:
ExecuteGenerateVa();
return false;
case TranslationStep.SplitVA:
ExecuteSplitVa();
return false;
case TranslationStep.LookupTLB:
ExecuteLookupTlb();
return false;
case TranslationStep.LookupPageTable:
ExecuteLookupPageTable();
return false;
case TranslationStep.HandlePageFault:
ExecuteHandlePageFault();
return false;
case TranslationStep.ComposePA:
ExecuteComposePa();
return false;
case TranslationStep.Finalize:
ExecuteFinalize();
return true;
default:
throw new InvalidOperationException($"未知翻译步骤:{State.CurrentStep}");
}
}
public AccessExecutionResult RunOneAccess(ulong? forcedVirtualAddress = null)
{
if (State.CurrentStep != TranslationStep.GenerateVA)
{
throw new InvalidOperationException("RunOneAccess 只能在 GenerateVA 起始步骤调用。");
}
if (forcedVirtualAddress.HasValue)
{
SetNextVirtualAddress(forcedVirtualAddress.Value);
}
while (!StepOnce())
{
}
return new AccessExecutionResult(
State.CurrentRound,
State.CurrentVirtualAddress,
State.CurrentAddressParts,
State.CurrentPfn,
State.CurrentPhysicalAddress,
State.IsTlbHit,
State.IsPageTableHit,
State.IsPageFault,
_lastEviction);
}
private void ExecuteGenerateVa()
{
State.IsTlbHit = false;
State.IsPageTableHit = false;
State.IsPageFault = false;
State.CurrentCost = 0;
State.CurrentAddressParts = AddressParts.Empty;
State.CurrentPhysicalAddress = 0UL;
State.CurrentPfn = 0UL;
State.HasCurrentPfn = false;
State.LastDecisionReason = "已生成本轮虚拟地址,准备进行地址拆分。";
_lastEviction = EvictionInfo.None;
if (_hasPendingForcedVirtualAddress)
{
State.CurrentVirtualAddress = _pendingForcedVirtualAddress;
_hasPendingForcedVirtualAddress = false;
_pendingForcedVirtualAddress = 0UL;
}
else
{
State.CurrentVirtualAddress = _addressGenerator.NextVirtualAddress();
}
State.CurrentStep = TranslationStep.SplitVA;
}
private void ExecuteSplitVa()
{
State.CurrentAddressParts = AddressTranslatorUtils.SplitVirtualAddress(
State.CurrentVirtualAddress,
_config.vaBits,
_derivedConfig.OffsetBits);
State.LastDecisionReason = "已拆分 VA 为 VPN/Offset并从 VPN 拆分出 L1/L2 索引。";
State.CurrentStep = TranslationStep.LookupTLB;
}
private void ExecuteLookupTlb()
{
if (_tlbCache.TryLookup(State.CurrentAddressParts.Vpn, out var pfn))
{
State.IsTlbHit = true;
State.CurrentPfn = pfn;
State.HasCurrentPfn = true;
State.LastDecisionReason =
$"TLB 命中VPN={State.CurrentAddressParts.Vpn} 命中 PFN={pfn},跳过页表,直接合成物理地址。";
State.CurrentStep = TranslationStep.ComposePA;
return;
}
State.LastDecisionReason =
$"TLB 未命中VPN={State.CurrentAddressParts.Vpn} 未找到映射,转入页表查询。";
State.CurrentStep = TranslationStep.LookupPageTable;
}
private void ExecuteLookupPageTable()
{
var probe = _pageTable.Probe(State.CurrentAddressParts.L1Index, State.CurrentAddressParts.L2Index);
if (probe.Status == PageTableProbeStatus.PresentEntry)
{
State.IsPageTableHit = true;
State.CurrentPfn = probe.Entry.Pfn;
State.HasCurrentPfn = true;
_tlbCache.InsertOrUpdate(State.CurrentAddressParts.Vpn, probe.Entry.Pfn);
State.LastDecisionReason =
$"页表命中L1={State.CurrentAddressParts.L1Index}, L2={State.CurrentAddressParts.L2Index}, PFN={probe.Entry.Pfn},并写回 TLB。";
State.CurrentStep = TranslationStep.ComposePA;
return;
}
State.LastDecisionReason = probe.Status switch
{
PageTableProbeStatus.MissingL1Table =>
$"页表未命中L1={State.CurrentAddressParts.L1Index} 对应二级页表不存在,触发缺页处理。",
PageTableProbeStatus.MissingL2Entry =>
$"页表未命中L2={State.CurrentAddressParts.L2Index} 页表项不存在,触发缺页处理。",
PageTableProbeStatus.NotPresentEntry =>
$"页表项无效L2={State.CurrentAddressParts.L2Index} present=false触发缺页处理。",
_ => "页表未命中,触发缺页处理。"
};
State.CurrentStep = TranslationStep.HandlePageFault;
}
private void ExecuteHandlePageFault()
{
var result = MemoryAccessResolver.Resolve(
State.CurrentAddressParts.Vpn,
State.CurrentAddressParts.L1Index,
State.CurrentAddressParts.L2Index,
_pageTable,
_physicalMemoryManager,
_tlbCache);
State.IsPageTableHit = result.PageTableHit;
State.IsPageFault = result.PageFault;
State.CurrentPfn = result.Pfn;
State.HasCurrentPfn = true;
_lastEviction = result.Eviction;
State.LastDecisionReason = result.Eviction.HasEvicted
? $"缺页处理完成:装入 PFN={result.Pfn},并按 FIFO 淘汰 VPN={result.Eviction.EvictedVpn}。"
: $"缺页处理完成:装入 PFN={result.Pfn},当前仍有空闲物理帧。";
State.CurrentStep = TranslationStep.ComposePA;
}
private void ExecuteComposePa()
{
if (!State.HasCurrentPfn)
{
throw new InvalidOperationException("ComposePA 阶段缺少 PFN无法合成物理地址。");
}
State.CurrentPhysicalAddress =
(State.CurrentPfn << _derivedConfig.OffsetBits) | State.CurrentAddressParts.Offset;
State.LastDecisionReason =
$"物理地址合成PA = (PFN << offsetBits) | offset = ({State.CurrentPfn} << {_derivedConfig.OffsetBits}) | {State.CurrentAddressParts.Offset}。";
State.CurrentStep = TranslationStep.Finalize;
}
private void ExecuteFinalize()
{
State.CurrentCost = Stats.CalculateCost(
State.IsTlbHit,
State.IsPageTableHit,
State.IsPageFault,
_config.pageFaultPenalty);
Stats.RecordAccess(
State.IsTlbHit,
State.IsPageTableHit,
State.IsPageFault,
State.CurrentCost);
State.LastDecisionReason = State.IsPageFault
? $"本轮完成TLB 未命中 + 页表未命中,发生缺页,开销={State.CurrentCost}。"
: State.IsTlbHit
? $"本轮完成TLB 命中,开销={State.CurrentCost}。"
: $"本轮完成TLB 未命中 + 页表命中,开销={State.CurrentCost}。";
State.CurrentRound += 1;
State.CurrentStep = TranslationStep.GenerateVA;
}
private void EnsureVirtualAddressInRange(ulong virtualAddress)
{
if (virtualAddress > _addressGenerator.MaxVirtualAddress)
{
throw new ArgumentOutOfRangeException(nameof(virtualAddress), "virtualAddress 超出 vaBits 可表示范围。");
}
}
private AddressGenerator CreateAddressGenerator()
{
return new AddressGenerator(
_config.vaBits,
_seed,
_config.addressGenerationMode,
_config.arrayLengthBytes,
_config.arrayElementBytes,
_config.arrayBaseAddress);
}
}
}