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:

  1. Poor interactivity: Single-key operations only, no mouse support
  2. Limited information display: Cannot see process tree relationships
  3. 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:

  1. Two samples: CPU usage must be calculated through two samples
  2. Time unit: Time in /proc/[pid]/stat is in clock ticks (usually 100Hz)
  3. 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 usage
  • PERCENT_MEM: Sort by memory usage
  • TIME: Sort by cumulative CPU time
  • STARTTIME: 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:

  1. Intuitive data: Color coding, bar charts, progress bars
  2. User-friendly interaction: Mouse operations, horizontal scrolling, tree view
  3. 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