363 lines
15 KiB
C#
363 lines
15 KiB
C#
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
|
|
/*******************************************************************/
|
|
/* WARNING */
|
|
/* This file should be identical in the Bartok and Singularity */
|
|
/* depots. Master copy resides in Bartok Depot. Changes should be */
|
|
/* made to Bartok Depot and propagated to Singularity Depot. */
|
|
/*******************************************************************/
|
|
|
|
namespace System.GCs {
|
|
|
|
using System.Runtime.CompilerServices;
|
|
using System.Runtime.InteropServices;
|
|
#if SINGULARITY_KERNEL
|
|
using Microsoft.Singularity;
|
|
using Microsoft.Singularity.Memory;
|
|
using Sing_MemoryManager = Microsoft.Singularity.Memory.MemoryManager;
|
|
#elif SINGULARITY_PROCESS
|
|
using Microsoft.Singularity;
|
|
using Microsoft.Singularity.V1.Services;
|
|
#endif
|
|
|
|
internal class MemoryManager
|
|
{
|
|
|
|
private static UIntPtr allocationGranularity;
|
|
private static UIntPtr totalMemorySize;
|
|
private static UIntPtr operatingSystemSize;
|
|
|
|
private static bool inAllocator;
|
|
|
|
#if SINGULARITY
|
|
|
|
internal static unsafe void Initialize()
|
|
{
|
|
allocationGranularity = (UIntPtr)0x10000;
|
|
totalMemorySize = PageTable.PageSize * PageTable.pageTableCount;
|
|
operatingSystemSize = totalMemorySize / 16;
|
|
}
|
|
|
|
//////////////////////////////////// Allocation and Free Routines.
|
|
//
|
|
// Allocation is optimized for the case where an allocation starts
|
|
// with a relatively small amount of memory and grows over time.
|
|
// This is exactly the behavior exhibited by stacks and GC heaps.
|
|
//
|
|
// The allocation strategy also works well for large initial
|
|
// allocations. The strategy would be very inefficient if a very
|
|
// large number of small, completely independent allocations are
|
|
// made.
|
|
//
|
|
// AllocateMemory(size) performs an initial allocation.
|
|
// AllocateMemory(startAddr, size) performs growing allocations.
|
|
//
|
|
internal static unsafe UIntPtr AllocateMemory(UIntPtr size)
|
|
{
|
|
VTable.Assert(PageTable.PageAligned(size));
|
|
#if SINGULARITY_KERNEL
|
|
UIntPtr addr = Sing_MemoryManager.KernelAllocate(
|
|
Sing_MemoryManager.PagesFromBytes(size),
|
|
Process.kernelProcess, 0, PageType.Unknown);
|
|
#elif SINGULARITY_PROCESS
|
|
UIntPtr addr = PageTableService.Allocate(size);
|
|
#endif
|
|
|
|
#if SINGULARITY_KERNEL
|
|
Kernel.Waypoint((int)size);
|
|
Kernel.Waypoint(811);
|
|
#endif // SINGULARITY_KERNEL
|
|
|
|
if (addr != UIntPtr.Zero) {
|
|
Util.MemClear(addr, size);
|
|
}
|
|
return addr;
|
|
}
|
|
|
|
internal static unsafe bool AllocateMemory(UIntPtr startAddr,
|
|
UIntPtr size)
|
|
{
|
|
VTable.Deny(inAllocator);
|
|
inAllocator = true;
|
|
VTable.Assert(PageTable.PageAligned(startAddr));
|
|
VTable.Assert(PageTable.PageAligned(size));
|
|
|
|
#if SINGULARITY_KERNEL
|
|
UIntPtr addr = Sing_MemoryManager.KernelExtend(
|
|
startAddr, Sing_MemoryManager.PagesFromBytes(size),
|
|
Process.kernelProcess, PageType.Unknown);
|
|
#elif SINGULARITY_PROCESS
|
|
UIntPtr addr = PageTableService.AllocateExtend(startAddr, size);
|
|
#endif
|
|
inAllocator = false;
|
|
if (addr != UIntPtr.Zero) {
|
|
Util.MemClear(addr, size);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
[Inline]
|
|
internal static void FreeMemory(UIntPtr startAddr, UIntPtr size)
|
|
{
|
|
#if SINGULARITY_KERNEL
|
|
DebugStub.Assert(Sing_MemoryManager.IsPageAligned(size));
|
|
Sing_MemoryManager.KernelFree(
|
|
startAddr, Sing_MemoryManager.PagesFromBytes(size),
|
|
Process.kernelProcess);
|
|
#elif SINGULARITY_PROCESS
|
|
VTable.Assert((size & PageTable.PageMask) == 0);
|
|
PageTableService.Free(startAddr, size);
|
|
#endif
|
|
}
|
|
|
|
internal static void IgnoreMemoryContents(UIntPtr startAddr,
|
|
UIntPtr regionSize)
|
|
{
|
|
// Since we don't do swapping, we simply ignore this information
|
|
}
|
|
|
|
internal static bool QueryMemory(UIntPtr queryAddr,
|
|
out UIntPtr regionAddr,
|
|
out UIntPtr regionSize)
|
|
{
|
|
#if SINGULARITY_KERNEL
|
|
PageType type = Sing_MemoryManager.KernelQuery(
|
|
queryAddr, out regionAddr, out regionSize);
|
|
return (type != PageType.Unknown);
|
|
#elif SINGULARITY_PROCESS
|
|
return PageTableService.Query(queryAddr, out regionAddr, out regionSize);
|
|
#endif
|
|
}
|
|
|
|
#else // not SINGULARITY
|
|
[NoBarriers]
|
|
[PreInitRefCounts]
|
|
[NoStackLinkCheckTrans]
|
|
internal static unsafe void Initialize() {
|
|
SYSTEM_INFO systemInfo;
|
|
GetSystemInfo(out systemInfo);
|
|
allocationGranularity =
|
|
new UIntPtr(systemInfo.AllocationGranularity);
|
|
MEMORYSTATUSEX memoryStatus = new MEMORYSTATUSEX();
|
|
memoryStatus.Length = (uint) sizeof(MEMORYSTATUSEX);
|
|
GlobalMemoryStatusEx(ref memoryStatus);
|
|
if (memoryStatus.TotalPhysical <= ((ulong)UIntPtr.MaxValue)) {
|
|
totalMemorySize = (UIntPtr) memoryStatus.TotalPhysical;
|
|
} else {
|
|
// TotalPhysical exceeds UIntPtr.MaxValue running in 32bit
|
|
// mode on a machine with more than 4GB of RAM
|
|
totalMemorySize = UIntPtr.MaxValue;
|
|
}
|
|
// We are just making a random guess here
|
|
operatingSystemSize = (UIntPtr) 1 << 26;
|
|
}
|
|
|
|
// Low-level routines based on Win32
|
|
|
|
internal const int PAGE_READWRITE = 0x04;
|
|
internal const int PAGE_EXECUTE_READWRITE = 0x40;
|
|
internal const int PAGE_NOACCESS = 0x01;
|
|
internal const int MEM_COMMIT = 0x1000;
|
|
internal const int MEM_RESERVE = 0x2000;
|
|
private const int MEM_DECOMMIT = 0x4000;
|
|
internal const int MEM_RELEASE = 0x8000;
|
|
private const int MEM_FREE = 0x10000;
|
|
private const int MEM_RESET = 0x80000;
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
private struct MEMORY_BASIC_INFORMATION {
|
|
internal UIntPtr BaseAddress;
|
|
internal UIntPtr AllocationBase;
|
|
internal int AllocationProtect;
|
|
internal UIntPtr RegionSize;
|
|
internal int State;
|
|
internal int Protect;
|
|
internal int Type;
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
private struct SYSTEM_INFO {
|
|
internal ushort ProcessorArchitecture;
|
|
internal ushort Reserved;
|
|
internal uint PageSize;
|
|
internal UIntPtr MinimumApplicationAddress;
|
|
internal UIntPtr MaximumApplicationAddress;
|
|
internal uint ActiveProcessorMask;
|
|
internal uint NumberOfProcessors;
|
|
internal uint ProcessorType;
|
|
internal uint AllocationGranularity;
|
|
internal ushort ProcessorLevel;
|
|
internal ushort ProcessorRevision;
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
private struct MEMORYSTATUSEX {
|
|
internal uint Length;
|
|
internal uint MemoryLoad;
|
|
internal ulong TotalPhysical;
|
|
internal ulong AvailPhysical;
|
|
internal ulong TotalPageFile;
|
|
internal ulong AvailPageFile;
|
|
internal ulong TotalVirtual;
|
|
internal ulong AvailVirtual;
|
|
internal ulong AvailExtendedVirtual;
|
|
}
|
|
|
|
// Kernel32.dll functions (via MSVCRT.dll)
|
|
[DllImport("BRT")]
|
|
[GCAnnotation(GCOption.NOGC)]
|
|
[NoStackLinkCheck]
|
|
[StackBound(1024)]
|
|
public static unsafe extern void *VirtualAlloc(void *lpAddress,
|
|
UIntPtr dwSize,
|
|
int fIAllocationType,
|
|
int fIProtect);
|
|
[DllImport("BRT")]
|
|
[GCAnnotation(GCOption.NOGC)]
|
|
[StackBound(1024)]
|
|
internal static unsafe extern int VirtualFree(void *lpAddress,
|
|
UIntPtr dwSize,
|
|
int dwFreeType);
|
|
|
|
[DllImport("BRT")]
|
|
[GCAnnotation(GCOption.NOGC)]
|
|
[StackBound(1024)]
|
|
private static unsafe extern UIntPtr VirtualQuery(void *lpAddress, out MEMORY_BASIC_INFORMATION memInfo, UIntPtr dwLength);
|
|
|
|
[DllImport("BRT")]
|
|
[GCAnnotation(GCOption.NOGC)]
|
|
[NoStackLinkCheck]
|
|
[StackBound(1024)]
|
|
[NoBarriers]
|
|
private static unsafe extern void GetSystemInfo(out SYSTEM_INFO systemInfo);
|
|
|
|
[DllImport("BRT")]
|
|
[GCAnnotation(GCOption.NOGC)]
|
|
[NoStackLinkCheck]
|
|
[StackBound(1024)]
|
|
[NoBarriers]
|
|
private static unsafe extern void GlobalMemoryStatusEx(ref MEMORYSTATUSEX memoryStatus);
|
|
|
|
// Low-level routines based on the operating system interface
|
|
|
|
private static unsafe UIntPtr AllocateMemoryHelper(UIntPtr startAddr,
|
|
UIntPtr size)
|
|
{
|
|
void *result = VirtualAlloc((void *) startAddr,
|
|
size, MEM_RESERVE, PAGE_READWRITE);
|
|
VTable.Assert
|
|
(PageTable.Page((UIntPtr) result + size - 1)
|
|
< PageTable.pageTableCount,
|
|
"OutOfMemory: MemoryManager: memory doesn't fit in page table");
|
|
if (result != null) {
|
|
Trace.Log(Trace.Area.Page,
|
|
"VirtualAlloc {0} at {1}",
|
|
__arglist(size, result));
|
|
VTable.Assert((startAddr == UIntPtr.Zero)
|
|
|| (result == (void *) startAddr));
|
|
|
|
void *area = VirtualAlloc(result, size,
|
|
MEM_COMMIT, PAGE_READWRITE);
|
|
if (PageTable.halPageDescriptor != null) {
|
|
PageTable.CreateNewPageTablesIfNecessary(PageTable.Page((UIntPtr)result), PageTable.PageCount(size));
|
|
PageTable.SetProcess(PageTable.Page((UIntPtr) area),
|
|
PageTable.PageCount(size));
|
|
}
|
|
VTable.Assert(result == area);
|
|
#if HIMEM
|
|
// This assertion intends only to catch bugs in Bartok. But if
|
|
// the system (windows) frees memory before, the memory
|
|
// may be in low memory, and if we happen to get that
|
|
// memory, this assertion itself may not hold
|
|
VTable.Assert((UIntPtr) result >= PageTable.HIMEMStart,
|
|
("High memory is expected to be allocated in HIMEM mode"));
|
|
#endif
|
|
}
|
|
return new UIntPtr(result);
|
|
}
|
|
|
|
internal static unsafe bool AllocateMemory(UIntPtr startAddr,
|
|
UIntPtr size) {
|
|
UIntPtr addr = AllocateMemoryHelper(startAddr, size);
|
|
return addr != UIntPtr.Zero;
|
|
}
|
|
|
|
internal static unsafe UIntPtr AllocateMemory(UIntPtr size) {
|
|
return AllocateMemoryHelper(UIntPtr.Zero, size);
|
|
}
|
|
|
|
internal static unsafe void FreeMemory(UIntPtr addr, UIntPtr size) {
|
|
Trace.Log(Trace.Area.Page,
|
|
"VirtualFree {0} at {1}",
|
|
__arglist(size, addr));
|
|
int success = VirtualFree((void *) addr, UIntPtr.Zero,
|
|
MEM_RELEASE);
|
|
VTable.Assert(success != 0);
|
|
}
|
|
|
|
// Indicate that we still own the memory but that we don't care
|
|
// about the content of the memory. The virtual memory system
|
|
// does not have to write the memory to the swap file in order
|
|
// to reuse the physical memory for other purposes.
|
|
internal static unsafe void IgnoreMemoryContents(UIntPtr startAddr,
|
|
UIntPtr regionSize)
|
|
{
|
|
Trace.Log(Trace.Area.Page,
|
|
"VirtualAlloc-Reset {0} at {1}",
|
|
__arglist(regionSize, startAddr));
|
|
void * result = VirtualAlloc((void *) startAddr, regionSize,
|
|
MEM_RESET, PAGE_READWRITE);
|
|
VTable.Assert(result == (void *) startAddr,
|
|
"VirtualAlloc MEM_RESET failed");
|
|
}
|
|
|
|
internal static unsafe bool QueryMemory(UIntPtr queryAddr,
|
|
out UIntPtr regionAddr,
|
|
out UIntPtr regionSize)
|
|
{
|
|
VTable.Assert(PageTable.PageAligned(queryAddr));
|
|
MEMORY_BASIC_INFORMATION memInfo;
|
|
UIntPtr size = (UIntPtr) sizeof(MEMORY_BASIC_INFORMATION);
|
|
UIntPtr data = VirtualQuery((void *) queryAddr, out memInfo, size);
|
|
Trace.Log(Trace.Area.Page,
|
|
"VirtualQuery {0}: base={1} size={2}",
|
|
__arglist(queryAddr, memInfo.AllocationBase,
|
|
memInfo.RegionSize));
|
|
if (data == 0) {
|
|
// queryAddr is a kernel-mode pointer
|
|
regionAddr = queryAddr;
|
|
regionSize = (UIntPtr) sizeof(int);
|
|
return false;
|
|
} else {
|
|
VTable.Assert(data == size &&
|
|
memInfo.BaseAddress == queryAddr);
|
|
regionAddr = memInfo.AllocationBase;
|
|
regionSize = (queryAddr - regionAddr) + memInfo.RegionSize;
|
|
return (memInfo.State != MEM_FREE);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
internal static UIntPtr OperatingSystemCommitSize {
|
|
get {
|
|
return allocationGranularity;
|
|
}
|
|
}
|
|
|
|
// We are just guessing here
|
|
internal static UIntPtr MemorySize {
|
|
get { return totalMemorySize; }
|
|
}
|
|
|
|
// We are just making an educated guess here
|
|
internal static UIntPtr OperatingSystemSize {
|
|
get { return operatingSystemSize; }
|
|
}
|
|
|
|
}
|
|
|
|
}
|