Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Linux System Monitor

A real-time system monitoring tool built in Python and C++ that reads directly from Linux kernel interfaces, no external libraries, no abstractions. Raw /proc filesystem parsing.

What It Does

Reads live system metrics every second:

  • CPU usage % — parsed from /proc/stat using time-delta calculation
  • Memory usage % — parsed from /proc/meminfo using MemAvailable
  • Network bandwidth (MB/s) — parsed from /proc/net/dev using byte-delta calculation
  • Top processes by memory — C++ binary reads /proc/[pid]/status directly

Why /proc?

/proc is a virtual filesystem exposed by the Linux kernel. It doesn't exist on disk, the kernel generates its contents in memory on every read. Tools like htop, top, and ps all read from /proc under the hood. This project does the same thing directly, without going through those tools.

Project Structure

systems-monitor/
├── monitor/
│   ├── __init__.py
│   ├── cpu.py           # reads /proc/stat, calculates CPU delta
│   ├── memory.py        # reads /proc/meminfo, calculates usage %
│   ├── network.py       # reads /proc/net/dev, calculates MB/s delta
│   └── monitor.py       # orchestrates all modules + calls C++ binary
├── cpp/
│   └── process_list.cpp # reads /proc/[pid]/status for each running PID
├── scripts/
│   └── run_monitor.sh   # bash daemon — start/stop/status
├── tests/
│   └── test_monitor.py  # 21 pytest tests across all modules
└── README.md

Linux Internals — How Each Module Works

CPU (/proc/stat)

cpu  1957 125 990 66371 236 0 72 0 0 0
      user nice sys idle iowait irq softirq

CPU usage cannot be calculated from a single snapshot, these are cumulative counters since boot. The module takes two readings separated by 1 second and calculates:

usage % = (1 - idle_delta / total_delta) * 100

idle_delta includes both idle and iowait, time the CPU was waiting for I/O counts as idle time. This is the same formula htop uses.

Memory (/proc/meminfo)

MemTotal:      3991132 kB
MemAvailable:  3534344 kB

The module uses MemAvailable not MemFree. The difference:

  • MemFree — RAM with absolutely nothing in it
  • MemAvailable — RAM available for new processes, includes reclaimable cache

MemFree is always misleadingly low because Linux deliberately fills RAM with filesystem cache. MemAvailable is what actually matters.

used % = (MemTotal - MemAvailable) / MemTotal * 100

Network (/proc/net/dev)

eth0: 119373906  95459  0  0  0  0  0  0  1122931  15786 ...
      rx_bytes   pkts  ...                tx_bytes  pkts

Like CPU, a single snapshot only gives total bytes since boot, not current bandwidth. The module takes two readings and calculates:

bandwidth (MB/s) = (bytes_2 - bytes_1) / interval / (1024 * 1024)

Loopback (lo) is excluded, it counts traffic twice and is not real network I/O.

Process List (C++ — /proc/[pid]/status)

/proc exposes a directory for every running process named by its PID. The C++ binary:

  1. Opens /proc and iterates over all numeric directory names (PIDs)
  2. Reads /proc/[pid]/status for each PID
  3. Extracts Name: and VmRSS: fields
  4. Filters out kernel threads (VmRSS = 0)
  5. Handles race conditions — processes can die mid-read

VmRSS (Virtual Memory Resident Set Size) is the actual physical RAM a process is using right now, not virtual memory, not reserved memory.

The Python orchestrator calls this binary via subprocess for clean language interop between the two layers.

Build & Run

Prerequisites

  • Linux (or Lima VM on macOS)
  • Python 3.8+
  • g++ compiler

Build C++ component

cd cpp && g++ -o process_list process_list.cpp && cd ..

Run once

python3 -m monitor.monitor

Run continuously

python3 -m monitor.monitor --watch

Run as background daemon

./scripts/run_monitor.sh start   # start
./scripts/run_monitor.sh status  # check
./scripts/run_monitor.sh stop    # stop

Run tests

pytest tests/test_monitor.py -v

Sample Output

=======================================================
           LINUX SYSTEM MONITOR
=======================================================

 CPU Usage:    0.75%

 Memory:
   Total:     3897.6 MB
   Used:      479.2 MB
   Available: 3418.4 MB
   Usage:     12.3%

 Network:
   eth0:
     RX: 0.0 MB/s
     TX: 0.0 MB/s

 Top Processes (by memory):
   PID      Name                    Memory
   -------- -------------------- ----------
   7859     containerd              40852 kB
   7983     buildkitd               40044 kB
   1021     unattended-upgr         28600 kB
   1787     lima-guestagent         27300 kB
   1        systemd                 14652 kB

=======================================================

Edge Cases Handled

  • Process dies between PID listing and file open → is_open() check
  • Process dies mid-read → empty name/zero memory filtered out
  • /proc fails to open → explicit error with non-zero exit code
  • Kernel threads → filtered by VmRSS == 0
  • Permission denied on some PIDs → handled by is_open() check
  • Division by zero in CPU calculation → explicit guard clause
  • Network interface disappears between readings → interface membership check

Key Concepts Demonstrated

  • Linux /proc virtual filesystem internals
  • CPU usage calculation via time-delta (same algorithm as htop)
  • MemAvailable vs MemFree - why the distinction matters
  • Network bandwidth as bytes-per-second delta
  • C++ and Python interop via subprocess
  • RAII in C++ - automatic resource cleanup
  • TOCTOU race conditions in systems programming
  • Bash daemon patterns - PID files, nohup, signal handling

About

Real-time Linux system monitor built in Python and C++, reads directly from /proc filesystem. No external dependencies.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages