Linux htop Command Deep Dive: Implementation Principles of Interactive Process Monitoring
Linux htop Command Deep Dive: Implementation Principles of Interactive Process Monitoring#
As an upgraded version of top, htop has become a daily tool for Linux system administrators thanks to its intuitive interface and powerful interactive capabilities. This article analyzes htop’s implementation from source code level and shares practical tips.
htop vs top: Core Differences#
Traditional top command has several pain points:
- Poor interactivity: Single-key operations only, no mouse support
- Limited information display: Cannot see process tree relationships
- Non-intuitive configuration: Requires memorizing complex configuration items
htop’s core improvements:
- Mouse click operations
- Horizontal/vertical scrolling to view more process information
- Tree view showing process relationships
- Color-coded process states
- Customizable display columns
Data Source: /proc Filesystem#
htop reads from the same source as top - the /proc virtual filesystem:
/proc/[pid]/stat # Process state, CPU time, memory, etc.
/proc/[pid]/statm # Memory page information
/proc/[pid]/cmdline # Command line arguments
/proc/[pid]/fd/ # Open file descriptors
/proc/meminfo # System total memory info
/proc/stat # CPU usage statistics
Core reading logic (pseudocode):
// Read process CPU time
void readProcessData(int pid) {
char path[64];
sprintf(path, "/proc/%d/stat", pid);
FILE* fp = fopen(path, "r");
unsigned long utime, stime;
fscanf(fp, "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %lu %lu",
&utime, &stime);
fclose(fp);
// utime: user mode time, stime: kernel mode time (unit: clock ticks)
}
CPU Usage Calculation Principle#
The core algorithm for calculating process CPU usage in htop:
// C implementation
struct ProcessStat {
unsigned long utime; // User mode time
unsigned long stime; // Kernel mode time
unsigned long total_time; // Total time
unsigned long timestamp; // Sample timestamp
};
float calculateCpuUsage(ProcessStat* prev, ProcessStat* curr) {
// Time delta between two samples
unsigned long time_delta = curr->total_time - prev->total_time;
unsigned long wall_delta = curr->timestamp - prev->timestamp;
// CPU usage = process time increment / wall time increment
// wall_delta needs to be converted to clock ticks
float usage = (float)time_delta / wall_delta * 100.0;
return usage;
}
Key points:
- Two samples: CPU usage must be calculated through two samples
- Time unit: Time in
/proc/[pid]/statis in clock ticks (usually 100Hz) - Multi-core handling: Usage can exceed 100% (multi-core parallelism)
Two Memory Metrics#
htop displays two memory indicators:
1. VIRT (Virtual Memory)#
VIRT = All memory requested by process
= Code segment + Data segment + Shared libraries + Stack + Heap + Mapped files
- Includes unallocated memory (malloc requested but unused)
- Includes shared library memory (shared across processes but counted for each)
2. RES (Resident Memory)#
RES = Actually used physical memory
= VIRT - unallocated - swapped out portion
This is the actual memory consumed by the process.
Reading code:
// Read from /proc/[pid]/statm
void readMemory(int pid, unsigned long* virt, unsigned long* res) {
char path[64];
sprintf(path, "/proc/%d/statm", pid);
FILE* fp = fopen(path, "r");
unsigned long size, resident;
fscanf(fp, "%lu %lu", &size, &resident);
*virt = size * sysconf(_SC_PAGESIZE); // Page size usually 4KB
*res = resident * sysconf(_SC_PAGESIZE);
fclose(fp);
}
Tree View Implementation#
The core of process tree is the PPID (parent process ID) field:
struct Process {
int pid;
int ppid;
char name[256];
struct Process* parent;
struct Process** children;
int child_count;
};
// Build process tree
void buildProcessTree(Process* processes, int count) {
// 1. Build PID -> Process mapping
Process* pidMap[MAX_PID] = {0};
for (int i = 0; i < count; i++) {
pidMap[processes[i].pid] = &processes[i];
}
// 2. Build parent-child relationships based on PPID
for (int i = 0; i < count; i++) {
Process* parent = pidMap[processes[i].ppid];
if (parent) {
processes[i].parent = parent;
// Add to parent's children list
parent->children[parent->child_count++] = &processes[i];
}
}
}
Practical Tips: Common Shortcuts#
Process Management#
| Shortcut | Function |
|---|---|
F9 |
Send signal (SIGTERM/SIGKILL, etc.) |
F7 |
Decrease priority (nice +1) |
F8 |
Increase priority (nice -1) |
Space |
Tag/untag process |
View Switching#
| Shortcut | Function |
|---|---|
F5 |
Toggle tree/list view |
F6 |
Select sort column |
F4 |
Filter processes (input process name) |
F3 |
Search process |
p |
Show full command line |
s |
Trace process system calls |
Sorting Tips#
Press F6 to select sort field:
PERCENT_CPU: Sort by CPU usagePERCENT_MEM: Sort by memory usageTIME: Sort by cumulative CPU timeSTARTTIME: Sort by start time
Performance Optimization: Why htop is Smoother than top#
htop uses ncurses library for efficient terminal rendering:
// ncurses partial refresh
void renderProcessList() {
for (int i = 0; i < visible_rows; i++) {
move(i + HEADER_ROWS, 0); // Move cursor
clrtoeol(); // Clear this line
printw("%5d %5s %3d%% %4dM %s",
process[i].pid,
process[i].user,
process[i].cpu,
process[i].mem,
process[i].name);
}
refresh(); // Only refresh changed areas
}
Compared to top’s full-screen refresh, htop only updates changed rows, reducing flicker.
Process States Explained#
htop uses different colors to indicate process states:
| State | Color | Meaning |
|---|---|---|
R (Running) |
Green | Currently running or runnable |
S (Sleeping) |
Blue | Interruptible sleep (waiting for event) |
D (Disk sleep) |
Red | Uninterruptible sleep (usually waiting for I/O) |
Z (Zombie) |
Gray | Zombie process (ended but parent hasn’t reaped) |
T (Stopped) |
Yellow | Stopped (SIGSTOP) |
Note on D state processes: If you find many processes in D state, it usually indicates disk I/O bottleneck or NFS mount issues.
Practical Scenarios#
1. Finding High CPU Processes#
# Method 1: Sort by CPU
htop -s PERCENT_CPU
# Method 2: Interactively press F6 to select PERCENT_CPU
2. Finding Memory Leak Processes#
# Sort by RES, observe processes with continuously growing memory
htop -s PERCENT_MEM
3. Monitoring Specific User’s Processes#
# Only show www-data user's processes
htop -u www-data
4. Tree View of Process Relationships#
# Start with tree view
htop -t
# Or press F5 during runtime to toggle
htop Configuration File#
Configuration saved in ~/.config/htop/htoprc:
# Enable mouse support
enable_mouse=1
# Default sort by CPU
sort_key=PERCENT_CPU
# Show threads
show_thread_names=0
# Color scheme
color_scheme=0
# Custom display columns
fields=PID USER PERCENT_CPU PERCENT_MEM COMM
Web Implementation: Browser-based htop#
To implement similar functionality in web:
// Node.js backend reads /proc
import { readFileSync, readdirSync } from 'fs'
function getProcessList() {
const pids = readdirSync('/proc').filter(d => /^\d+$/.test(d))
return pids.map(pid => {
const stat = readFileSync(`/proc/${pid}/stat`, 'utf-8')
const parts = stat.split(' ')
return {
pid: parseInt(parts[0]),
name: parts[1].replace(/\(|\)/g, ''),
state: parts[2],
ppid: parseInt(parts[3]),
utime: parseInt(parts[13]),
stime: parseInt(parts[14]),
rss: parseInt(parts[23]) * 4 // pages to KB
}
})
}
// WebSocket push to frontend
setInterval(() => {
wss.clients.forEach(client => {
client.send(JSON.stringify(getProcessList()))
})
}, 1000)
Frontend uses React to render table with virtual scrolling for large datasets.
Summary#
htop’s core values are:
- Intuitive data: Color coding, bar charts, progress bars
- User-friendly interaction: Mouse operations, horizontal scrolling, tree view
- Rich features: Filtering, searching, sending signals, modifying priority
Understanding its underlying principles (/proc filesystem + ncurses rendering) helps quickly identify issues when problems arise.
Related: Linux ps Process Monitoring | Linux top Real-time Monitoring