Step 4 - 二级页表 + 缺页 + FIFO
This commit is contained in:
parent
bda18464c8
commit
38aa8e9bf4
|
|
@ -0,0 +1,74 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace VMdemo.Simulation
|
||||||
|
{
|
||||||
|
public readonly struct MemoryAccessResult
|
||||||
|
{
|
||||||
|
public MemoryAccessResult(ulong pfn, bool pageTableHit, bool pageFault, EvictionInfo eviction)
|
||||||
|
{
|
||||||
|
Pfn = pfn;
|
||||||
|
PageTableHit = pageTableHit;
|
||||||
|
PageFault = pageFault;
|
||||||
|
Eviction = eviction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ulong Pfn { get; }
|
||||||
|
public bool PageTableHit { get; }
|
||||||
|
public bool PageFault { get; }
|
||||||
|
public EvictionInfo Eviction { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class MemoryAccessResolver
|
||||||
|
{
|
||||||
|
public static MemoryAccessResult Resolve(
|
||||||
|
ulong vpn,
|
||||||
|
ulong l1Index,
|
||||||
|
ulong l2Index,
|
||||||
|
TwoLevelPageTable pageTable,
|
||||||
|
PhysicalMemoryManager physicalMemoryManager,
|
||||||
|
TlbCache tlbCache)
|
||||||
|
{
|
||||||
|
if (pageTable == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(pageTable));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (physicalMemoryManager == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(physicalMemoryManager));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tlbCache == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(tlbCache));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pageTable.TryGetPresentPfn(l1Index, l2Index, out var pfn))
|
||||||
|
{
|
||||||
|
tlbCache.InsertOrUpdate(vpn, pfn);
|
||||||
|
return new MemoryAccessResult(pfn, true, false, EvictionInfo.None);
|
||||||
|
}
|
||||||
|
|
||||||
|
var allocation = physicalMemoryManager.AllocateForVpn(vpn);
|
||||||
|
if (allocation.Eviction.HasEvicted)
|
||||||
|
{
|
||||||
|
pageTable.GetIndicesFromVpn(
|
||||||
|
allocation.Eviction.EvictedVpn,
|
||||||
|
out var evictedL1Index,
|
||||||
|
out var evictedL2Index);
|
||||||
|
|
||||||
|
pageTable.MarkNotPresent(evictedL1Index, evictedL2Index);
|
||||||
|
tlbCache.Remove(allocation.Eviction.EvictedVpn);
|
||||||
|
}
|
||||||
|
|
||||||
|
pageTable.SetPresent(l1Index, l2Index, allocation.AssignedPfn);
|
||||||
|
tlbCache.InsertOrUpdate(vpn, allocation.AssignedPfn);
|
||||||
|
|
||||||
|
return new MemoryAccessResult(
|
||||||
|
allocation.AssignedPfn,
|
||||||
|
pageTableHit: false,
|
||||||
|
pageFault: true,
|
||||||
|
allocation.Eviction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 69a345783d80681488273b665f20247f
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace VMdemo.Simulation
|
||||||
|
{
|
||||||
|
[Serializable]
|
||||||
|
public readonly struct PageTableEntry
|
||||||
|
{
|
||||||
|
public PageTableEntry(ulong pfn, bool present, bool dirty = false)
|
||||||
|
{
|
||||||
|
Pfn = pfn;
|
||||||
|
Present = present;
|
||||||
|
Dirty = dirty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ulong Pfn { get; }
|
||||||
|
public bool Present { get; }
|
||||||
|
public bool Dirty { get; }
|
||||||
|
|
||||||
|
public static PageTableEntry NotPresent => new PageTableEntry(0UL, false, false);
|
||||||
|
|
||||||
|
public PageTableEntry MarkPresent(ulong pfn, bool dirty = false)
|
||||||
|
{
|
||||||
|
return new PageTableEntry(pfn, true, dirty);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PageTableEntry MarkNotPresent()
|
||||||
|
{
|
||||||
|
return new PageTableEntry(Pfn, false, Dirty);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return $"PFN={Pfn}, Present={Present}, Dirty={Dirty}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 319e4b22dc36b754db1b833626fa2681
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -0,0 +1,133 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace VMdemo.Simulation
|
||||||
|
{
|
||||||
|
public readonly struct EvictionInfo
|
||||||
|
{
|
||||||
|
public EvictionInfo(bool hasEvicted, ulong evictedVpn, ulong evictedPfn)
|
||||||
|
{
|
||||||
|
HasEvicted = hasEvicted;
|
||||||
|
EvictedVpn = evictedVpn;
|
||||||
|
EvictedPfn = evictedPfn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool HasEvicted { get; }
|
||||||
|
public ulong EvictedVpn { get; }
|
||||||
|
public ulong EvictedPfn { get; }
|
||||||
|
|
||||||
|
public static EvictionInfo None => new EvictionInfo(false, 0UL, 0UL);
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly struct AllocationResult
|
||||||
|
{
|
||||||
|
public AllocationResult(ulong assignedPfn, bool wasAlreadyResident, EvictionInfo eviction)
|
||||||
|
{
|
||||||
|
AssignedPfn = assignedPfn;
|
||||||
|
WasAlreadyResident = wasAlreadyResident;
|
||||||
|
Eviction = eviction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ulong AssignedPfn { get; }
|
||||||
|
public bool WasAlreadyResident { get; }
|
||||||
|
public EvictionInfo Eviction { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PhysicalMemoryManager
|
||||||
|
{
|
||||||
|
private readonly Queue<ResidentPage> _fifoQueue = new Queue<ResidentPage>();
|
||||||
|
private readonly Dictionary<ulong, ulong> _residentByVpn = new Dictionary<ulong, ulong>();
|
||||||
|
private ulong _nextFreeFrame;
|
||||||
|
|
||||||
|
public PhysicalMemoryManager(ulong frameCount)
|
||||||
|
{
|
||||||
|
if (frameCount < 1UL)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(frameCount), "frameCount 必须 >= 1。");
|
||||||
|
}
|
||||||
|
|
||||||
|
FrameCount = frameCount;
|
||||||
|
_nextFreeFrame = 0UL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ulong FrameCount { get; }
|
||||||
|
public int ResidentCount => _residentByVpn.Count;
|
||||||
|
|
||||||
|
public bool TryGetResidentPfn(ulong vpn, out ulong pfn)
|
||||||
|
{
|
||||||
|
return _residentByVpn.TryGetValue(vpn, out pfn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AllocationResult AllocateForVpn(ulong vpn)
|
||||||
|
{
|
||||||
|
if (_residentByVpn.TryGetValue(vpn, out var existingPfn))
|
||||||
|
{
|
||||||
|
return new AllocationResult(existingPfn, true, EvictionInfo.None);
|
||||||
|
}
|
||||||
|
|
||||||
|
EvictionInfo evictionInfo;
|
||||||
|
ulong assignedPfn;
|
||||||
|
|
||||||
|
if (_nextFreeFrame < FrameCount)
|
||||||
|
{
|
||||||
|
assignedPfn = _nextFreeFrame;
|
||||||
|
_nextFreeFrame++;
|
||||||
|
evictionInfo = EvictionInfo.None;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (_fifoQueue.Count == 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("内存已满但 FIFO 队列为空,状态异常。");
|
||||||
|
}
|
||||||
|
|
||||||
|
var victim = _fifoQueue.Dequeue();
|
||||||
|
if (!_residentByVpn.Remove(victim.Vpn))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("FIFO 淘汰项未出现在驻留映射中,状态异常。");
|
||||||
|
}
|
||||||
|
|
||||||
|
assignedPfn = victim.Pfn;
|
||||||
|
evictionInfo = new EvictionInfo(true, victim.Vpn, victim.Pfn);
|
||||||
|
}
|
||||||
|
|
||||||
|
_residentByVpn[vpn] = assignedPfn;
|
||||||
|
_fifoQueue.Enqueue(new ResidentPage(vpn, assignedPfn));
|
||||||
|
return new AllocationResult(assignedPfn, false, evictionInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<ulong> GetFifoVpnOrder()
|
||||||
|
{
|
||||||
|
var order = new List<ulong>(_fifoQueue.Count);
|
||||||
|
foreach (var page in _fifoQueue)
|
||||||
|
{
|
||||||
|
order.Add(page.Vpn);
|
||||||
|
}
|
||||||
|
|
||||||
|
return order;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string FormatFifoQueue()
|
||||||
|
{
|
||||||
|
var order = GetFifoVpnOrder();
|
||||||
|
if (order.Count == 0)
|
||||||
|
{
|
||||||
|
return "FIFO=[]";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $"FIFO=[{string.Join(",", order)}]";
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly struct ResidentPage
|
||||||
|
{
|
||||||
|
public ResidentPage(ulong vpn, ulong pfn)
|
||||||
|
{
|
||||||
|
Vpn = vpn;
|
||||||
|
Pfn = pfn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ulong Vpn { get; }
|
||||||
|
public ulong Pfn { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 442eea92195560643874961592e5317d
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -0,0 +1,163 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace VMdemo.Simulation
|
||||||
|
{
|
||||||
|
public class TwoLevelPageTable
|
||||||
|
{
|
||||||
|
private readonly Dictionary<ulong, Dictionary<ulong, PageTableEntry>> _table;
|
||||||
|
private readonly ulong _l1Mask;
|
||||||
|
private readonly ulong _l2Mask;
|
||||||
|
private readonly ulong _vpnMask;
|
||||||
|
|
||||||
|
public TwoLevelPageTable(int l1Bits, int l2Bits)
|
||||||
|
{
|
||||||
|
if (l1Bits < 0 || l1Bits > 64)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(l1Bits), "l1Bits 必须在 0 到 64 之间。");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (l2Bits < 0 || l2Bits > 64)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(l2Bits), "l2Bits 必须在 0 到 64 之间。");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (l1Bits + l2Bits <= 0 || l1Bits + l2Bits > 64)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(l2Bits), "l1Bits + l2Bits 必须在 1 到 64 之间。");
|
||||||
|
}
|
||||||
|
|
||||||
|
L1Bits = l1Bits;
|
||||||
|
L2Bits = l2Bits;
|
||||||
|
_table = new Dictionary<ulong, Dictionary<ulong, PageTableEntry>>();
|
||||||
|
_l1Mask = GetBitMask(l1Bits);
|
||||||
|
_l2Mask = GetBitMask(l2Bits);
|
||||||
|
_vpnMask = GetBitMask(l1Bits + l2Bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int L1Bits { get; }
|
||||||
|
public int L2Bits { get; }
|
||||||
|
|
||||||
|
public bool TryGetPresentPfn(ulong l1Index, ulong l2Index, out ulong pfn)
|
||||||
|
{
|
||||||
|
ValidateIndices(l1Index, l2Index);
|
||||||
|
|
||||||
|
if (!_table.TryGetValue(l1Index, out var l2Table))
|
||||||
|
{
|
||||||
|
pfn = 0UL;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!l2Table.TryGetValue(l2Index, out var entry) || !entry.Present)
|
||||||
|
{
|
||||||
|
pfn = 0UL;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pfn = entry.Pfn;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetEntry(ulong l1Index, ulong l2Index, out PageTableEntry entry)
|
||||||
|
{
|
||||||
|
ValidateIndices(l1Index, l2Index);
|
||||||
|
|
||||||
|
if (_table.TryGetValue(l1Index, out var l2Table) &&
|
||||||
|
l2Table.TryGetValue(l2Index, out var value))
|
||||||
|
{
|
||||||
|
entry = value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry = PageTableEntry.NotPresent;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetPresent(ulong l1Index, ulong l2Index, ulong pfn, bool dirty = false)
|
||||||
|
{
|
||||||
|
ValidateIndices(l1Index, l2Index);
|
||||||
|
|
||||||
|
if (!_table.TryGetValue(l1Index, out var l2Table))
|
||||||
|
{
|
||||||
|
l2Table = new Dictionary<ulong, PageTableEntry>();
|
||||||
|
_table[l1Index] = l2Table;
|
||||||
|
}
|
||||||
|
|
||||||
|
l2Table[l2Index] = new PageTableEntry(pfn, true, dirty);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool MarkNotPresent(ulong l1Index, ulong l2Index)
|
||||||
|
{
|
||||||
|
ValidateIndices(l1Index, l2Index);
|
||||||
|
|
||||||
|
if (!_table.TryGetValue(l1Index, out var l2Table) ||
|
||||||
|
!l2Table.TryGetValue(l2Index, out var entry))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!entry.Present)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
l2Table[l2Index] = entry.MarkNotPresent();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GetIndicesFromVpn(ulong vpn, out ulong l1Index, out ulong l2Index)
|
||||||
|
{
|
||||||
|
if (L1Bits + L2Bits < 64 && (vpn & ~_vpnMask) != 0UL)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(vpn), "vpn 超出页表可表示范围。");
|
||||||
|
}
|
||||||
|
|
||||||
|
l2Index = vpn & _l2Mask;
|
||||||
|
l1Index = L2Bits >= 64 ? 0UL : vpn >> L2Bits;
|
||||||
|
ValidateIndices(l1Index, l2Index);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ValidateIndices(ulong l1Index, ulong l2Index)
|
||||||
|
{
|
||||||
|
if (!IsWithinBits(l1Index, L1Bits, _l1Mask))
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(l1Index), "l1Index 超出 L1 位宽范围。");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsWithinBits(l2Index, L2Bits, _l2Mask))
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(l2Index), "l2Index 超出 L2 位宽范围。");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsWithinBits(ulong value, int bits, ulong mask)
|
||||||
|
{
|
||||||
|
if (bits == 64)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bits == 0)
|
||||||
|
{
|
||||||
|
return value == 0UL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (value & ~mask) == 0UL;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ulong GetBitMask(int bits)
|
||||||
|
{
|
||||||
|
if (bits <= 0)
|
||||||
|
{
|
||||||
|
return 0UL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bits >= 64)
|
||||||
|
{
|
||||||
|
return ulong.MaxValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (1UL << bits) - 1UL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2ee7fcf06ce017648a41c05cc07db305
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -0,0 +1,134 @@
|
||||||
|
using System;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using VMdemo.Simulation;
|
||||||
|
|
||||||
|
namespace VMdemo.Tests.EditMode
|
||||||
|
{
|
||||||
|
public class Step4PageTableAndFifoTests
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void TwoLevelPageTable_SetPresent_ThenTryGetPresentPfn_Hits()
|
||||||
|
{
|
||||||
|
var pageTable = new TwoLevelPageTable(3, 2);
|
||||||
|
pageTable.SetPresent(5UL, 3UL, 9UL, dirty: true);
|
||||||
|
|
||||||
|
var hit = pageTable.TryGetPresentPfn(5UL, 3UL, out var pfn);
|
||||||
|
var exists = pageTable.TryGetEntry(5UL, 3UL, out var entry);
|
||||||
|
|
||||||
|
Assert.IsTrue(hit);
|
||||||
|
Assert.That(pfn, Is.EqualTo(9UL));
|
||||||
|
Assert.IsTrue(exists);
|
||||||
|
Assert.That(entry.Present, Is.True);
|
||||||
|
Assert.That(entry.Dirty, Is.True);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TwoLevelPageTable_MarkNotPresent_ThenMisses()
|
||||||
|
{
|
||||||
|
var pageTable = new TwoLevelPageTable(3, 2);
|
||||||
|
pageTable.SetPresent(1UL, 2UL, 7UL);
|
||||||
|
|
||||||
|
var changed = pageTable.MarkNotPresent(1UL, 2UL);
|
||||||
|
var changedAgain = pageTable.MarkNotPresent(1UL, 2UL);
|
||||||
|
var hitAfterMark = pageTable.TryGetPresentPfn(1UL, 2UL, out _);
|
||||||
|
var exists = pageTable.TryGetEntry(1UL, 2UL, out var entry);
|
||||||
|
|
||||||
|
Assert.IsTrue(changed);
|
||||||
|
Assert.IsFalse(changedAgain);
|
||||||
|
Assert.IsFalse(hitAfterMark);
|
||||||
|
Assert.IsTrue(exists);
|
||||||
|
Assert.IsFalse(entry.Present);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void PhysicalMemoryManager_AllocateForVpn_EvictsInFifoOrder()
|
||||||
|
{
|
||||||
|
var manager = new PhysicalMemoryManager(2UL);
|
||||||
|
|
||||||
|
var r1 = manager.AllocateForVpn(1UL);
|
||||||
|
var r2 = manager.AllocateForVpn(2UL);
|
||||||
|
var r3 = manager.AllocateForVpn(3UL); // evict 1
|
||||||
|
var r4 = manager.AllocateForVpn(4UL); // evict 2
|
||||||
|
|
||||||
|
Assert.That(r1.AssignedPfn, Is.EqualTo(0UL));
|
||||||
|
Assert.That(r2.AssignedPfn, Is.EqualTo(1UL));
|
||||||
|
Assert.That(r3.AssignedPfn, Is.EqualTo(0UL));
|
||||||
|
Assert.That(r4.AssignedPfn, Is.EqualTo(1UL));
|
||||||
|
|
||||||
|
Assert.IsFalse(r1.Eviction.HasEvicted);
|
||||||
|
Assert.IsFalse(r2.Eviction.HasEvicted);
|
||||||
|
Assert.IsTrue(r3.Eviction.HasEvicted);
|
||||||
|
Assert.That(r3.Eviction.EvictedVpn, Is.EqualTo(1UL));
|
||||||
|
Assert.That(r3.Eviction.EvictedPfn, Is.EqualTo(0UL));
|
||||||
|
Assert.IsTrue(r4.Eviction.HasEvicted);
|
||||||
|
Assert.That(r4.Eviction.EvictedVpn, Is.EqualTo(2UL));
|
||||||
|
Assert.That(r4.Eviction.EvictedPfn, Is.EqualTo(1UL));
|
||||||
|
|
||||||
|
var fifoOrder = manager.GetFifoVpnOrder();
|
||||||
|
Assert.That(fifoOrder.Count, Is.EqualTo(2));
|
||||||
|
Assert.That(fifoOrder[0], Is.EqualTo(3UL));
|
||||||
|
Assert.That(fifoOrder[1], Is.EqualTo(4UL));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Resolve_WhenEvictionHappens_SyncsPageTableAndTlb()
|
||||||
|
{
|
||||||
|
var pageTable = new TwoLevelPageTable(4, 4);
|
||||||
|
var memory = new PhysicalMemoryManager(1UL);
|
||||||
|
var tlb = new TlbCache(4);
|
||||||
|
|
||||||
|
const ulong vpn1 = 1UL;
|
||||||
|
pageTable.GetIndicesFromVpn(vpn1, out var l1_1, out var l2_1);
|
||||||
|
var first = MemoryAccessResolver.Resolve(vpn1, l1_1, l2_1, pageTable, memory, tlb);
|
||||||
|
|
||||||
|
Assert.IsTrue(first.PageFault);
|
||||||
|
Assert.IsFalse(first.PageTableHit);
|
||||||
|
Assert.IsFalse(first.Eviction.HasEvicted);
|
||||||
|
Assert.IsTrue(tlb.Lookup(vpn1, out _));
|
||||||
|
|
||||||
|
var second = MemoryAccessResolver.Resolve(vpn1, l1_1, l2_1, pageTable, memory, tlb);
|
||||||
|
Assert.IsFalse(second.PageFault);
|
||||||
|
Assert.IsTrue(second.PageTableHit);
|
||||||
|
|
||||||
|
const ulong vpn2 = 2UL;
|
||||||
|
pageTable.GetIndicesFromVpn(vpn2, out var l1_2, out var l2_2);
|
||||||
|
var third = MemoryAccessResolver.Resolve(vpn2, l1_2, l2_2, pageTable, memory, tlb);
|
||||||
|
|
||||||
|
Assert.IsTrue(third.PageFault);
|
||||||
|
Assert.IsTrue(third.Eviction.HasEvicted);
|
||||||
|
Assert.That(third.Eviction.EvictedVpn, Is.EqualTo(vpn1));
|
||||||
|
Assert.IsFalse(tlb.Lookup(vpn1, out _));
|
||||||
|
Assert.IsTrue(tlb.Lookup(vpn2, out _));
|
||||||
|
|
||||||
|
pageTable.GetIndicesFromVpn(vpn1, out var evictedL1, out var evictedL2);
|
||||||
|
Assert.IsFalse(pageTable.TryGetPresentPfn(evictedL1, evictedL2, out _));
|
||||||
|
Assert.IsFalse(memory.TryGetResidentPfn(vpn1, out _));
|
||||||
|
Assert.IsTrue(memory.TryGetResidentPfn(vpn2, out _));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void HighVolumeAccess_N10000_RunsWithoutStateCorruption()
|
||||||
|
{
|
||||||
|
var pageTable = new TwoLevelPageTable(8, 8);
|
||||||
|
var memory = new PhysicalMemoryManager(16UL);
|
||||||
|
var tlb = new TlbCache(8);
|
||||||
|
var random = new Random(123);
|
||||||
|
|
||||||
|
for (var i = 0; i < 10000; i++)
|
||||||
|
{
|
||||||
|
var vpn = (ulong)random.Next(0, 512);
|
||||||
|
pageTable.GetIndicesFromVpn(vpn, out var l1, out var l2);
|
||||||
|
|
||||||
|
var result = MemoryAccessResolver.Resolve(vpn, l1, l2, pageTable, memory, tlb);
|
||||||
|
var fifoOrder = memory.GetFifoVpnOrder();
|
||||||
|
|
||||||
|
Assert.That(memory.ResidentCount, Is.LessThanOrEqualTo(16));
|
||||||
|
Assert.That(fifoOrder.Count, Is.EqualTo(memory.ResidentCount));
|
||||||
|
if (result.PageTableHit)
|
||||||
|
{
|
||||||
|
Assert.IsFalse(result.PageFault);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 03c7b540fc1fd394bb518328af03654b
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -102,22 +102,22 @@
|
||||||
|
|
||||||
### TODO
|
### TODO
|
||||||
|
|
||||||
- [ ] 新建 `TwoLevelPageTable.cs`:`Dictionary<L1, Dictionary<L2, PTE>>`
|
- [x] 新建 `TwoLevelPageTable.cs`:`Dictionary<L1, Dictionary<L2, PTE>>`
|
||||||
- [ ] 新建 `PageTableEntry.cs`:`PFN / present / dirty(可选)`
|
- [x] 新建 `PageTableEntry.cs`:`PFN / present / dirty(可选)`
|
||||||
- [ ] 新建 `PhysicalMemoryManager.cs`:空闲帧管理 + FIFO 队列
|
- [x] 新建 `PhysicalMemoryManager.cs`:空闲帧管理 + FIFO 队列
|
||||||
- [ ] 缺页时分配空闲帧;无空闲帧时 FIFO 淘汰
|
- [x] 缺页时分配空闲帧;无空闲帧时 FIFO 淘汰
|
||||||
- [ ] 淘汰后同步页表项状态(`present = false`)
|
- [x] 淘汰后同步页表项状态(`present = false`)
|
||||||
|
|
||||||
### 行为要求
|
### 行为要求
|
||||||
|
|
||||||
- [ ] 页表命中:TLB 未命中后,页表中有 `present = true` 映射
|
- [x] 页表命中:TLB 未命中后,页表中有 `present = true` 映射
|
||||||
- [ ] 缺页:页表不存在或 `present = false`
|
- [x] 缺页:页表不存在或 `present = false`
|
||||||
- [ ] 装入页面后写回页表并更新 TLB
|
- [x] 装入页面后写回页表并更新 TLB
|
||||||
|
|
||||||
### 完成标准
|
### 完成标准
|
||||||
|
|
||||||
- [ ] 能稳定运行大量访问(例如 `N=10000`)无崩溃
|
- [x] 能稳定运行大量访问(例如 `N=10000`)无崩溃
|
||||||
- [ ] FIFO 次序可从日志中验证
|
- [x] FIFO 次序可从日志中验证
|
||||||
|
|
||||||
## 6. Step 5 - 翻译引擎(单步状态机)
|
## 6. Step 5 - 翻译引擎(单步状态机)
|
||||||
|
|
||||||
|
|
|
||||||
Reference in New Issue