CX Framework
Cross-platform C utility framework
Loading...
Searching...
No Matches

Two sets of readings live here: how busy the whole machine is, and how busy one process is. Both are plain structures filled in by a single call, so nothing needs to be initialized or destroyed – declare one, pass its address, read the fields.

if (sysMemInfo(&mem)) {
// mem.physTotal bytes of memory are installed
}
bool sysMemInfo(SysMemInfo *out)
How much memory the machine has and how much of it is spoken for.
Definition sysinfo.h:126

What the system will and will not tell you

Operating systems do not all count the same things. FreeBSD keeps no record of committed memory, Windows does not report how much of a process has been paged out, and only Linux separates time spent waiting for a disk from time spent idle.

Every structure therefore carries a valid field: a set of flags naming the fields the operating system actually reported. A field it did not report is also left at zero, so code that only wants a rough number can ignore valid entirely and print the zero. Code that needs to tell "nothing is being used" apart from "nobody is counting" checks the flag.

if (mem.valid & SYSMEM_Commit) {
// mem.commitTotal and mem.commitAvail are real numbers
}
@ SYSMEM_Commit
commitTotal and commitAvail
Definition sysinfo.h:120
flags_t valid
SysMemInfoValid flags naming the fields that were reported.
Definition sysinfo.h:128

Each field's own documentation says where it is unavailable.

A call that could report nothing at all on this platform – sysLoadAvg() on Windows is the only one – returns false and sets cxerr to CX_NotSupported instead of returning an empty structure. A call that can report something always returns true, with valid saying how much.

Processor time is a running total

sysCPUTimes() and procCPUTimes() report how much processor time has been used since the machine, or the process, started. A single reading cannot tell you how busy anything is right now; two readings and the time between them can.

sysCPUUsage() and procCPUUsage() do that arithmetic. Both return a percentage of the whole machine's capacity, from 0 to 100, so the two are directly comparable: a process reading of 25 means the process is using a quarter of everything the machine can do. Multiply by osLogicalCPUs() for the per-core figure that tools like top print, where a process using four cores fully reads 400.

SysCPUTimes before, after;
sysCPUTimes(&before);
sysCPUTimes(&after);
float64 busy = sysCPUUsage(&before, &after);
void osSleep(int64 time)
float64 sysCPUUsage(const SysCPUTimes *first, const SysCPUTimes *second)
bool sysCPUTimes(SysCPUTimes *out)
#define timeS(s)
Definition time.h:20

For code that polls in a loop, a sampler keeps the previous reading so only one call is needed each time round. It has no answer to give until it has been called twice, and says so by returning false.

SysCPUSampler sampler;
for (;;) {
float64 busy;
if (sysCPUSample(&sampler, &busy)) {
// the machine was 'busy' percent loaded since the last time round
}
}
bool sysCPUSample(SysCPUSampler *s, float64 *pct)
#define sysCPUSamplerInit(s)
Definition sysinfo.h:385
Remembers the previous reading so repeated polling needs only one call each time.
Definition sysinfo.h:369

Samplers hold nothing but a previous reading, so they need no cleanup, and a process sampler does not hold the process either – it is passed in on each call.

Reading other processes

The per-process calls come in two forms: one taking a Process handle, and one taking a process id directly. Both ask the operating system for the least access that will answer the question, and neither ever asks for more rights than it was started with. A reading that needs privileges the caller does not have comes back as a cleared flag, or as false with cxerr set to CX_AccessDenied, never as an attempt to acquire those privileges.

In practice processor and memory figures can be read for any process on the machine, while I/O counters on Linux can only be read for processes belonging to the same user.

Every reading is a snapshot

The numbers describe the moment the call was made. Memory in particular moves constantly, so two fields read by two separate calls will not agree exactly, and a process can exit between one call and the next.

A handle whose process has already finished reports no statistics: the calls return false with cxerr set to CX_FileNotFound rather than reporting whatever now holds that process id.