126 lines
4.6 KiB
C#
126 lines
4.6 KiB
C#
using System.Net.NetworkInformation;
|
||
using System.Runtime.InteropServices;
|
||
using System.Runtime.Versioning;
|
||
|
||
namespace MoShiro.Measurement;
|
||
|
||
/// <summary>
|
||
/// Windows 上读主机资源占用(<see cref="HostSampler"/> 在 Linux 之外的实现):CPU 时间和物理内存用两个只读的系统调用
|
||
/// (<c>GetSystemTimes</c>、<c>GlobalMemoryStatusEx</c>),网卡流量和链路速率用 .NET 自带的 <see cref="NetworkInterface"/>。
|
||
/// 和 Linux 一样只有汇总的数字:不读网卡名、MAC、IP,也不启动任何进程。
|
||
/// </summary>
|
||
[SupportedOSPlatform("windows")]
|
||
internal static partial class WindowsHost
|
||
{
|
||
private static readonly string[] s_virtualWords = ["Virtual", "vEthernet", "Hyper-V", "VMware", "VirtualBox", "TAP-", "Wintun", "WireGuard", "Npcap", "Bluetooth", "Loopback", "isatap", "Teredo"];
|
||
|
||
[StructLayout(LayoutKind.Sequential)]
|
||
private struct FileTime
|
||
{
|
||
public uint Low;
|
||
public uint High;
|
||
|
||
public readonly long Value => (long)(((ulong)High << 32) | Low);
|
||
}
|
||
|
||
[StructLayout(LayoutKind.Sequential)]
|
||
private struct MemoryStatus
|
||
{
|
||
public uint Length;
|
||
public uint MemoryLoad;
|
||
public ulong TotalPhys;
|
||
public ulong AvailPhys;
|
||
public ulong TotalPageFile;
|
||
public ulong AvailPageFile;
|
||
public ulong TotalVirtual;
|
||
public ulong AvailVirtual;
|
||
public ulong AvailExtendedVirtual;
|
||
}
|
||
|
||
[LibraryImport("kernel32.dll", SetLastError = true)]
|
||
[return: MarshalAs(UnmanagedType.Bool)]
|
||
private static partial bool GetSystemTimes(out FileTime idle, out FileTime kernel, out FileTime user);
|
||
|
||
[LibraryImport("kernel32.dll", SetLastError = true)]
|
||
[return: MarshalAs(UnmanagedType.Bool)]
|
||
private static partial bool GlobalMemoryStatusEx(ref MemoryStatus status);
|
||
|
||
/// <summary>系统启动以来的 CPU 总时间和空闲时间(和 Linux 的 <c>/proc/stat</c> 第一行同一个意思:两次采样相减得到占用)。</summary>
|
||
public static (long Total, long Idle)? Cpu()
|
||
{
|
||
try
|
||
{
|
||
if (!GetSystemTimes(out FileTime idle, out FileTime kernel, out FileTime user))
|
||
{
|
||
return null;
|
||
}
|
||
|
||
// 内核时间里已经包含了空闲时间
|
||
return (kernel.Value + user.Value, idle.Value);
|
||
}
|
||
catch (Exception ex) when (ex is DllNotFoundException or EntryPointNotFoundException)
|
||
{
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/// <summary>物理内存的总量和已用(总量减去可用)。</summary>
|
||
public static (long Total, long Used)? Memory()
|
||
{
|
||
try
|
||
{
|
||
var status = new MemoryStatus { Length = (uint)Marshal.SizeOf<MemoryStatus>() };
|
||
if (!GlobalMemoryStatusEx(ref status) || status.TotalPhys == 0)
|
||
{
|
||
return null;
|
||
}
|
||
|
||
return ((long)status.TotalPhys, (long)(status.TotalPhys - Math.Min(status.AvailPhys, status.TotalPhys)));
|
||
}
|
||
catch (Exception ex) when (ex is DllNotFoundException or EntryPointNotFoundException)
|
||
{
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/// <summary>所有物理网卡(在用的,排除回环、隧道、虚拟网卡)的累计收发字节,以及链路速率之和(Mbit/s;有一块读不到速率就是 null)。</summary>
|
||
public static (long Rx, long Tx, long? LinkMbps)? Network()
|
||
{
|
||
try
|
||
{
|
||
long rx = 0;
|
||
long tx = 0;
|
||
long mbps = 0;
|
||
bool speedKnown = true;
|
||
bool any = false;
|
||
foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces())
|
||
{
|
||
if (nic.OperationalStatus != OperationalStatus.Up || nic.NetworkInterfaceType is NetworkInterfaceType.Loopback or NetworkInterfaceType.Tunnel
|
||
|| s_virtualWords.Any(w => nic.Description.Contains(w, StringComparison.OrdinalIgnoreCase) || nic.Name.Contains(w, StringComparison.OrdinalIgnoreCase)))
|
||
{
|
||
continue;
|
||
}
|
||
|
||
IPInterfaceStatistics stats = nic.GetIPStatistics();
|
||
rx += stats.BytesReceived;
|
||
tx += stats.BytesSent;
|
||
any = true;
|
||
if (nic.Speed > 0)
|
||
{
|
||
mbps += nic.Speed / 1_000_000;
|
||
}
|
||
else
|
||
{
|
||
speedKnown = false;
|
||
}
|
||
}
|
||
|
||
return any ? (rx, tx, speedKnown && mbps > 0 ? mbps : null) : null;
|
||
}
|
||
catch (NetworkInformationException)
|
||
{
|
||
return null;
|
||
}
|
||
}
|
||
}
|