Reading values

edit

Values that can be absent

A getter that reads a kernel file returns a CGroupValue<T> rather than a nullable value, because a value can be absent for different reasons, which call for different decisions. "The memory controller is not enabled" and "the memory is unlimited" must not be confused by code that decides whether to apply its own limit.

var parent = CGroup2.Root.CreateOrGetChild("myapp-values");
parent.SetControllers("memory");
var worker = parent.CreateOrGetChild("worker");

// memory.max holds "max": there is no limit
var memoryMax = worker.GetMemoryMax();
Assert.Equal(CGroupValueState.NotConfigured, memoryMax.State);
Assert.Equal("max", memoryMax.RawValue);

worker.SetMemoryMax(512L * 1024 * 1024);
memoryMax = worker.GetMemoryMax();
Assert.Equal(CGroupValueState.Configured, memoryMax.State);
Assert.Equal(512L * 1024 * 1024, memoryMax.Value);

// The pids controller is not enabled on the parent, so pids.max does not exist
var pidsMax = worker.GetPidsMax();
Assert.Equal(CGroupValueState.Unavailable, pidsMax.State);
Assert.False(pidsMax.TryGetValue(out _));
Assert.Equal(long.MaxValue, pidsMax.GetValueOrDefault(long.MaxValue));

worker.Delete();
parent.Delete();

State tells what the file holds:

StateMeaning
ConfiguredThe file holds a value: Value returns it
NotConfiguredThe file holds no limit (max)
UnavailableThe file does not exist: the controller is not enabled on the parent, or the kernel does not support the feature
InvalidThe file holds content the library does not understand; RawValue keeps it

Read the value the way that fits your code:

  • switch on State when the cases call for different actions.
  • TryGetValue when only a value matters.
  • GetValueOrDefault(fallback) when a fallback is good enough.
  • Value throws an InvalidOperationException when the state is not Configured.

RawValue gives the content of the file without its surrounding whitespace, or null when the file does not exist.

Limits and usage

GetterReturns
GetCpuWeight()The CPU weight
GetCpuMax()A CpuMax: MaxMicroseconds, null when unlimited, and PeriodMicroseconds. The state is never NotConfigured, because the period is meaningful without a limit
GetMemoryMax(), GetMemoryHigh(), GetMemoryLow(), GetMemoryMin(), GetSwapMax()The memory limits, in bytes
GetMemoryCurrent()The memory the group uses, in bytes
GetPidsMax(), GetPidsCurrent()The process limit and the current number of processes
GetCpusetCpus(), GetCpusetCpusEffective(), GetCpusetMems(), GetCpusetMemsEffective()The CPUs and memory nodes
GetCpusetPartition()The partition type
GetHugeTlbMax(pageSize), GetHugeTlbCurrent(pageSize), GetHugeTlbEventsMax(pageSize)The huge page limit, usage and refused allocations

Statistics

GetCpuStat and GetMemoryStat read the statistics of the group:

var group = CGroup2.Root.CreateOrGetChild("myapp-statistics");

// TryGetValue when only a value matters
if (group.GetCpuStat().TryGetValue(out var cpuStat))
{
    var cpuTime = TimeSpan.FromMicroseconds(cpuStat.UsageMicroseconds);
    Assert.True(cpuTime >= TimeSpan.Zero);
}

// GetValueOrDefault when a fallback is good enough
var memoryInBytes = group.GetMemoryCurrent().GetValueOrDefault(0);
Assert.True(memoryInBytes >= 0);

group.Delete();

To watch a group, read these values periodically: the CPU time grows, so the difference between two readings divided by the elapsed time gives the CPU usage.

Numbers

Numbers are read and written with the invariant culture, whatever the culture of the application.