Building My Own Resource Monitor - Pulse


My laptop’s SSD was dying, read/write speeds had plummeted to mere KBs per second, making development on Windows an exercise in agony. After the service center gave up, I took matters into my own hands. I wiped the drive and jumped into the blazingly fast world of Arch Linux (btw).
This new environment was a revelation. I swapped my desktop for the i3 tiling window manager and fine-tuned Neovim into a powerhouse. As I settled in, I looked for a system monitor. In Windows, I had the graphical Task Manager, but here, I found the text-based tools top
and htop
. Staring at their updating columns of data, a simple question hit me: how do they actually work? That spark of curiosity was the real beginning.
Enter Pulse: my attempt to answer that question by building my own resource monitor from scratch in C.
Hard Mode Rules
To force myself to learn deeply, I set strict limits:
Dev Tools: Neovim + Makefile automation only.
Documentation: No LLMs. My sources were the Linux man pages and Stack Overflow.
Debugging: Only
gdb
andvalgrind
to catch runtime bugs and memory leaks.Logs: A dedicated Notion logbook for progress, pitfalls, and parsing discoveries.
Version 1: Parsing the Kernel’s Pulse
The first version had no UI, just raw data extraction from the /proc
filesystem. This meant getting my hands dirty with low-level C functions. I used:
opendir
/readdir
to iterate through all numeric directories (PIDs) inside/proc
.strtok_r
andsscanf
to safely parse lines in crucial files like/proc/[pid]/stat
,/proc/meminfo
, and/proc/cpuinfo
.Custom struct definitions in header files to cleanly store all the parsed stats.
The core loop for finding processes became second nature:
DIR *proc_dir = opendir("/proc");
if (!proc_dir) return 0;
while((entry = readdir(proc_dir)) != NULL && count < capacity ){
if(!isdigit(entry->d_name[0])) continue;
...
}
Hand-parsing each field with sscanf was far slower than using a library, but it was infinitely more educational. By the end, I had a clean, leak-free C program that could read the live vitals of my system.
Version 2: Giving Pulse a Terminal Face
With data collection solved, I wanted Pulse to look and feel like a real tool. I chose ncurses
, even though its API is famously brutal.
Initially, my terminal would break after every segfault. I quickly learned that forgetting to call endwin()
forces you to blindly type reset and pray. After many such lessons, I had a working interface:
A header window displaying system-wide memory and CPU info.
A scrollable body window for the process list, color-coded by state.
A manually crafted update loop: fetch, clear, draw, and refresh.
It finally looked like something real. But it was slow. The UI would visibly freeze forever, pressing buttons didn’t register at all, killing the user experience and forcing the user to close the terminal.
Version 3 : Making It Live
To fix the lag, I drew on my Android development experience: separate the UI on the main thread from computations on another. I implemented the classic Producer-Consumer pattern using POSIX
threads:
Producer thread: A background worker whose only job is to parse /proc and populate the data structs.
Consumer thread (Main): This thread is now lean, only responsible for rendering the UI and handling user input.
Shared data access between the threads was protected using a pthread_mutex
. Now, updates ran fluidly in the background. I could scroll through processes, sort them, and interact instantly while the stats updated seamlessly. Pulse finally felt alive.
What’s Next : Vim-ify Everything
We live in an age where AI can abstract away complexity and generate code in an instant, but I believe the most valuable knowledge is the kind that's earned through struggle. Pulse is working, but this is only the beginning. My roadmap is focused on one thing: making it a paradise for keyboard purists.
Modal Controls:
j
/k
to navigate,/
to search, and:q
to quit, just like Vim.Process Pinning: The ability to lock a specific process at the top of the list for easy tracking.
Threading Optimizations: I plan to investigate
condition variables
andasync polling
to reduce lock contention and make it even more efficient.
Everything’s open source and ready for you to explore. I'd love to hear your ideas.
Check it out on GitHub: github.com/Shresth-Deorari/Pulsef
Subscribe to my newsletter
Read articles from Shresth Deorari directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
