You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

165 lines
6.6 KiB

using System.Diagnostics;
using System.Runtime.Versioning;
using Microsoft.Extensions.DependencyInjection;
using WTA.Shared.Attributes;
using WTA.Shared.DependencyInjection;
using WTA.Shared.Extensions;
namespace WTA.Shared.Monitor;
[Implement<IMonitorService>(ServiceLifetime.Singleton, PlatformType.Linux)]
[SupportedOSPlatform("linux")]
public class LinuxService : BaseService, IMonitorService
{
private readonly CancellationTokenSource _cts;
private readonly Stopwatch _stopWatch;
private MonitorModel _model;
private LinuxStatusModel? _prevStatus;
public LinuxService()
{
this._model = base.CreateModel();
this._cts = new CancellationTokenSource();
this._stopWatch = new Stopwatch();
Task.Run(async () =>
{
while (!this._cts.IsCancellationRequested)
{
if (!this._stopWatch.IsRunning)
{
this._stopWatch.Start();
this._prevStatus = this.DoWorkInternal();
}
else
{
this._stopWatch.Stop();
this.DoWork();
this._stopWatch.Reset();
}
await Task.Delay(1000 * 1).ConfigureAwait(false);
}
}, this._cts.Token);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
this._cts.Cancel();
}
public MonitorModel GetStatus()
{
return this._model;
}
private void DoWork()
{
if (this._prevStatus != null)
{
this._model = base.CreateModel();
var status = this.DoWorkInternal();
var microseconds = this._stopWatch.Elapsed.TotalMicroseconds;
//整体CPU占用率
this._model.CpuUsage = 1 - 1.0 * (status.CpuIdle - this._prevStatus.CpuIdle) / (status.CpuTotal - this._prevStatus.CpuTotal);
//进程CPU占用率:计算采用的时间间隔和系统的CPU占用率计算一致
this._model.ProcessCpuUsage = 1.0 * (status.ProcessCpuUsed - this._prevStatus.ProcessCpuUsed) / (status.CpuTotal - this._prevStatus.CpuTotal);
//整体网络
this._model.NetReceived = (float)(1.0f * (status.NetReceived - this._prevStatus.NetReceived) / (microseconds / 1_000_000));
this._model.NetSent = (float)(1.0f * (status.NetSent - this._prevStatus.NetSent) / (microseconds / 1_000_000));
//整体磁盘读写
this._model.DiskRead = (float)(1.0f * (status.DiskRead - this._prevStatus.DiskRead) / (microseconds / 1_000_000));
this._model.DiskWrite = (float)(1.0f * (status.DiskWrite - this._prevStatus.DiskWrite) / (microseconds / 1_000_000));
//进程磁盘读写速率
this._model.ProcessDiskRead = (float)(1.0f * (status.ProcessDiskRead - this._prevStatus.ProcessDiskRead) / (microseconds / 1_000_000));
this._model.ProcessDiskWrite = (float)(1.0f * (status.ProcessDiskWrite - this._prevStatus.ProcessDiskWrite) / (microseconds / 1_000_000));
//
this._prevStatus = status;
}
}
protected virtual LinuxStatusModel DoWorkInternal()
{
var status = new LinuxStatusModel();
var microseconds = this._stopWatch.Elapsed.TotalMicroseconds;
// cpu
var procStat = File.ReadAllLines("/proc/stat");
var statValues = procStat.First().ToValues()
.Skip(1)
.Select(o => o.ToInt())
.ToArray();
status.CpuTotal = statValues.Sum();
status.CpuIdle = statValues[3];
// process cpu
var processProcStat = File.ReadAllLines($"/proc/{base.CurrentProcess.Id}/stat");
var processStatValues = processProcStat.First().ToValues()
.Skip(13)
.Take(9)
.Select(o => o.ToInt())
.ToArray();
status.ProcessCpuUsed = processStatValues.Take(2).Sum();
// memory
var procMeminfo = File.ReadAllLines("/proc/meminfo")
.Take(2)
.Select(o => o.ToValues())
.Where(o => o.Length == 3)
.Select(o => new KeyValuePair<string, long>(o[0], o[1].ToLong()))
.ToDictionary(o => o.Key, o => o.Value);
this._model.TotalMemory = procMeminfo["MemTotal:"] * 1024;
this._model.MemoryUsage = 1 - 1.0 * procMeminfo["MemFree:"] * 1024 / this._model.TotalMemory;
// network
var procNetDev = File.ReadAllLines("/proc/net/dev").Skip(2)
.Select(o => o.ToValues())
.Where(o => o[0] != "lo")
.Select(o => new { Receive = o[1].ToLong(), Transmit = o[9].ToLong() })
.ToList();
var inBytes = procNetDev.Sum(o => o.Receive);
var outBytes = procNetDev.Sum(o => o.Transmit);
status.NetReceived = inBytes;
status.NetSent = outBytes;
// thread
this._model.ThreadCount = Process.GetProcesses().Select(o => o.Id)
.ToArray()
.Select(pid => File.ReadAllText($"/proc/{pid}/stat").ToValues()[19].ToInt())
.Sum();
// disk
// https://www.kernel.org/doc/Documentation/iostats.txt
// https://www.kernel.org/doc/Documentation/block/stat.txt
var diskValues = File.ReadLines("/proc/diskstats").Select(o => o.ToValues())
.Where(o => o.Length >= 3 && o[2].StartsWith("sd"))
.Select(o => o[2].Trim())
.ToList()
.Select(o => File.ReadAllText($"/sys/block/{o}/stat").ToValues())
.Select(o => new { ReadBytes = o[2].ToLong() * 512, WriteBytes = o[6].ToLong() * 512 })
.ToList();
status.DiskRead = diskValues.Sum(o => o.ReadBytes);
status.DiskWrite = diskValues.Sum(o => o.WriteBytes);
//
var procPidIO = File.ReadLines($"/proc/{base.CurrentProcess.Id}/io")
.Select(o => o.ToValues())
//.Skip(4)
.Select(o => new KeyValuePair<string, long>(o[0], o[1].ToLong()))
.ToDictionary(o => o.Key, o => o.Value);
status.ProcessDiskRead = procPidIO["rchar:"];
status.ProcessDiskWrite = procPidIO["wchar:"];
//
return status;
}
}
public class LinuxStatusModel
{
public int CpuIdle { get; set; }
public int CpuTotal { get; set; }
public long NetReceived { get; set; }
public long NetSent { get; set; }
public int ProcessCpuUsed { get; set; }
public long DiskRead { get; set; }
public long DiskWrite { get; set; }
public long ProcessDiskRead { get; set; }
public long ProcessDiskWrite { get; set; }
}