Skip to main content
The Process struct provides methods to retrieve I/O statistics including read/write operations and byte counts for disk and other I/O operations.

IOCounters

Function Signature

func (p *Process) IOCounters() (*IOCountersStat, error)
func (p *Process) IOCountersWithContext(ctx context.Context) (*IOCountersStat, error)
Returns I/O statistics for the process.

Return Value

Returns a pointer to IOCountersStat:
type IOCountersStat struct {
    ReadCount      uint64 `json:"readCount"`      // Number of read operations
    WriteCount     uint64 `json:"writeCount"`     // Number of write operations
    ReadBytes      uint64 `json:"readBytes"`      // Bytes read (all I/O)
    WriteBytes     uint64 `json:"writeBytes"`     // Bytes written (all I/O)
    DiskReadBytes  uint64 `json:"diskReadBytes"`  // Bytes read from disk (Linux only)
    DiskWriteBytes uint64 `json:"diskWriteBytes"` // Bytes written to disk (Linux only)
}

Field Descriptions

ReadCount
uint64
Number of read I/O operations performed (syscalls like read(), pread(), etc.)
WriteCount
uint64
Number of write I/O operations performed (syscalls like write(), pwrite(), etc.)
ReadBytes
uint64
Total bytes read from all sources including disk, network, pipes, etc. (on Linux and Windows)
WriteBytes
uint64
Total bytes written to all destinations including disk, network, pipes, etc. (on Linux and Windows)
DiskReadBytes
uint64
Bytes actually read from disk storage (Linux only, from /proc/[pid]/io)
DiskWriteBytes
uint64
Bytes actually written to disk storage (Linux only, from /proc/[pid]/io)
ReadBytes and WriteBytes include all I/O including cached reads/writes, while DiskReadBytes and DiskWriteBytes (Linux only) represent actual physical disk I/O.

Usage Examples

Basic I/O Statistics

package main

import (
    "fmt"
    "github.com/shirou/gopsutil/v4/process"
)

func main() {
    p, err := process.NewProcess(1234)
    if err != nil {
        panic(err)
    }
    
    io, err := p.IOCounters()
    if err != nil {
        panic(err)
    }
    
    fmt.Printf("I/O Statistics:\n")
    fmt.Printf("  Read Operations:  %d\n", io.ReadCount)
    fmt.Printf("  Write Operations: %d\n", io.WriteCount)
    fmt.Printf("  Bytes Read:       %d MB\n", io.ReadBytes/1024/1024)
    fmt.Printf("  Bytes Written:    %d MB\n", io.WriteBytes/1024/1024)
    
    // Linux-specific disk I/O
    if io.DiskReadBytes > 0 || io.DiskWriteBytes > 0 {
        fmt.Printf("\nDisk I/O (Linux):\n")
        fmt.Printf("  Disk Read:  %d MB\n", io.DiskReadBytes/1024/1024)
        fmt.Printf("  Disk Write: %d MB\n", io.DiskWriteBytes/1024/1024)
    }
}

Monitor I/O Over Time

package main

import (
    "fmt"
    "time"
    "github.com/shirou/gopsutil/v4/process"
)

func monitorIO(pid int32) {
    p, err := process.NewProcess(pid)
    if err != nil {
        panic(err)
    }
    
    name, _ := p.Name()
    fmt.Printf("Monitoring I/O for %s (PID %d)\n", name, pid)
    fmt.Println("========================================")
    
    // Get initial values
    prevIO, _ := p.IOCounters()
    prevTime := time.Now()
    
    ticker := time.NewTicker(5 * time.Second)
    defer ticker.Stop()
    
    for range ticker.C {
        currentIO, err := p.IOCounters()
        if err != nil {
            fmt.Printf("Error: %v\n", err)
            return
        }
        
        currentTime := time.Now()
        elapsed := currentTime.Sub(prevTime).Seconds()
        
        // Calculate deltas
        readDelta := currentIO.ReadBytes - prevIO.ReadBytes
        writeDelta := currentIO.WriteBytes - prevIO.WriteBytes
        readOpsDelta := currentIO.ReadCount - prevIO.ReadCount
        writeOpsDelta := currentIO.WriteCount - prevIO.WriteCount
        
        // Calculate rates (bytes per second)
        readRate := float64(readDelta) / elapsed
        writeRate := float64(writeDelta) / elapsed
        
        fmt.Printf("[%s]\n", currentTime.Format("15:04:05"))
        fmt.Printf("  Read:  %8.2f KB/s (%d ops)\n", 
            readRate/1024, readOpsDelta)
        fmt.Printf("  Write: %8.2f KB/s (%d ops)\n", 
            writeRate/1024, writeOpsDelta)
        
        prevIO = currentIO
        prevTime = currentTime
    }
}

func main() {
    monitorIO(1234)
}

I/O Intensive Process Detector

package main

import (
    "fmt"
    "sort"
    "time"
    "github.com/shirou/gopsutil/v4/process"
)

type ProcessIO struct {
    PID        int32
    Name       string
    ReadRate   float64
    WriteRate  float64
    TotalRate  float64
}

func findIOIntensiveProcesses(topN int) {
    pids, _ := process.Pids()
    
    // First snapshot
    type snapshot struct {
        io   *process.IOCountersStat
        time time.Time
    }
    
    snapshots := make(map[int32]snapshot)
    
    // Take initial measurements
    for _, pid := range pids {
        p, err := process.NewProcess(pid)
        if err != nil {
            continue
        }
        
        io, err := p.IOCounters()
        if err != nil {
            continue
        }
        
        snapshots[pid] = snapshot{io: io, time: time.Now()}
    }
    
    // Wait for measurement interval
    fmt.Println("Measuring I/O activity...")
    time.Sleep(3 * time.Second)
    
    var processes []ProcessIO
    
    // Take second measurements and calculate rates
    for pid, snap1 := range snapshots {
        p, err := process.NewProcess(pid)
        if err != nil {
            continue
        }
        
        name, _ := p.Name()
        
        io2, err := p.IOCounters()
        if err != nil {
            continue
        }
        
        elapsed := time.Since(snap1.time).Seconds()
        
        readRate := float64(io2.ReadBytes-snap1.io.ReadBytes) / elapsed
        writeRate := float64(io2.WriteBytes-snap1.io.WriteBytes) / elapsed
        
        if readRate > 0 || writeRate > 0 {
            processes = append(processes, ProcessIO{
                PID:       pid,
                Name:      name,
                ReadRate:  readRate,
                WriteRate: writeRate,
                TotalRate: readRate + writeRate,
            })
        }
    }
    
    // Sort by total I/O rate
    sort.Slice(processes, func(i, j int) bool {
        return processes[i].TotalRate > processes[j].TotalRate
    })
    
    fmt.Printf("\nTop %d I/O-intensive processes:\n\n", topN)
    fmt.Printf("%-8s %-25s %15s %15s %15s\n", 
        "PID", "Name", "Read (KB/s)", "Write (KB/s)", "Total (KB/s)")
    fmt.Println("--------------------------------------------------------------------------------")
    
    for i := 0; i < topN && i < len(processes); i++ {
        p := processes[i]
        fmt.Printf("%-8d %-25s %14.2f %14.2f %14.2f\n",
            p.PID,
            p.Name,
            p.ReadRate/1024,
            p.WriteRate/1024,
            p.TotalRate/1024)
    }
}

func main() {
    findIOIntensiveProcesses(10)
}

Database Process I/O Monitor

package main

import (
    "fmt"
    "time"
    "github.com/shirou/gopsutil/v4/process"
)

func monitorDatabaseIO(pid int32, alertThresholdMBps float64) {
    p, err := process.NewProcess(pid)
    if err != nil {
        panic(err)
    }
    
    name, _ := p.Name()
    fmt.Printf("Monitoring database I/O: %s (PID %d)\n", name, pid)
    fmt.Printf("Alert threshold: %.2f MB/s\n", alertThresholdMBps)
    fmt.Println("========================================")
    
    prevIO, _ := p.IOCounters()
    prevTime := time.Now()
    
    ticker := time.NewTicker(10 * time.Second)
    defer ticker.Stop()
    
    for range ticker.C {
        currentIO, err := p.IOCounters()
        if err != nil {
            fmt.Printf("Error: %v\n", err)
            return
        }
        
        currentTime := time.Now()
        elapsed := currentTime.Sub(prevTime).Seconds()
        
        // Calculate rates in MB/s
        readMBps := float64(currentIO.ReadBytes-prevIO.ReadBytes) / elapsed / 1024 / 1024
        writeMBps := float64(currentIO.WriteBytes-prevIO.WriteBytes) / elapsed / 1024 / 1024
        totalMBps := readMBps + writeMBps
        
        // Calculate IOPS
        readIOPS := float64(currentIO.ReadCount-prevIO.ReadCount) / elapsed
        writeIOPS := float64(currentIO.WriteCount-prevIO.WriteCount) / elapsed
        
        fmt.Printf("\n[%s]\n", currentTime.Format("2006-01-02 15:04:05"))
        fmt.Printf("  Throughput:\n")
        fmt.Printf("    Read:  %8.2f MB/s\n", readMBps)
        fmt.Printf("    Write: %8.2f MB/s\n", writeMBps)
        fmt.Printf("    Total: %8.2f MB/s\n", totalMBps)
        fmt.Printf("  IOPS:\n")
        fmt.Printf("    Read:  %8.2f ops/s\n", readIOPS)
        fmt.Printf("    Write: %8.2f ops/s\n", writeIOPS)
        
        // Linux-specific: Show disk I/O vs cached I/O
        if currentIO.DiskReadBytes > 0 {
            diskReadMBps := float64(currentIO.DiskReadBytes-prevIO.DiskReadBytes) / elapsed / 1024 / 1024
            diskWriteMBps := float64(currentIO.DiskWriteBytes-prevIO.DiskWriteBytes) / elapsed / 1024 / 1024
            
            fmt.Printf("  Physical Disk I/O:\n")
            fmt.Printf("    Read:  %8.2f MB/s\n", diskReadMBps)
            fmt.Printf("    Write: %8.2f MB/s\n", diskWriteMBps)
            
            cacheReadMBps := readMBps - diskReadMBps
            cacheWriteMBps := writeMBps - diskWriteMBps
            
            if cacheReadMBps > 0 || cacheWriteMBps > 0 {
                fmt.Printf("  Cached I/O:\n")
                fmt.Printf("    Read:  %8.2f MB/s\n", cacheReadMBps)
                fmt.Printf("    Write: %8.2f MB/s\n", cacheWriteMBps)
            }
        }
        
        // Alert on high I/O
        if totalMBps > alertThresholdMBps {
            fmt.Printf("\n⚠️  HIGH I/O ALERT: %.2f MB/s exceeds threshold of %.2f MB/s\n",
                totalMBps, alertThresholdMBps)
        }
        
        prevIO = currentIO
        prevTime = currentTime
    }
}

func main() {
    monitorDatabaseIO(1234, 50.0) // Alert if I/O exceeds 50 MB/s
}

Cumulative I/O Report

package main

import (
    "fmt"
    "github.com/shirou/gopsutil/v4/process"
)

func ioReport(pid int32) {
    p, err := process.NewProcess(pid)
    if err != nil {
        panic(err)
    }
    
    name, _ := p.Name()
    createTime, _ := p.CreateTime()
    io, err := p.IOCounters()
    if err != nil {
        panic(err)
    }
    
    // Calculate process uptime
    uptime := time.Since(time.Unix(0, createTime*int64(time.Millisecond)))
    uptimeHours := uptime.Hours()
    
    fmt.Printf("I/O Report for %s (PID %d)\n", name, pid)
    fmt.Println("==========================================")
    fmt.Printf("Process uptime: %.2f hours\n\n", uptimeHours)
    
    fmt.Println("Cumulative I/O:")
    fmt.Printf("  Read Operations:  %d\n", io.ReadCount)
    fmt.Printf("  Write Operations: %d\n", io.WriteCount)
    fmt.Printf("  Total Operations: %d\n\n", io.ReadCount+io.WriteCount)
    
    fmt.Printf("  Bytes Read:    %d (%.2f GB)\n", 
        io.ReadBytes, float64(io.ReadBytes)/1024/1024/1024)
    fmt.Printf("  Bytes Written: %d (%.2f GB)\n", 
        io.WriteBytes, float64(io.WriteBytes)/1024/1024/1024)
    fmt.Printf("  Total I/O:     %.2f GB\n\n", 
        float64(io.ReadBytes+io.WriteBytes)/1024/1024/1024)
    
    // Average rates over process lifetime
    if uptimeHours > 0 {
        avgReadMBph := float64(io.ReadBytes) / uptimeHours / 1024 / 1024
        avgWriteMBph := float64(io.WriteBytes) / uptimeHours / 1024 / 1024
        
        fmt.Println("Average Rates:")
        fmt.Printf("  Read:  %.2f MB/hour\n", avgReadMBph)
        fmt.Printf("  Write: %.2f MB/hour\n", avgWriteMBph)
    }
    
    // Average operation sizes
    if io.ReadCount > 0 {
        avgReadSize := float64(io.ReadBytes) / float64(io.ReadCount) / 1024
        fmt.Printf("\nAverage read size: %.2f KB\n", avgReadSize)
    }
    if io.WriteCount > 0 {
        avgWriteSize := float64(io.WriteBytes) / float64(io.WriteCount) / 1024
        fmt.Printf("Average write size: %.2f KB\n", avgWriteSize)
    }
}

func main() {
    ioReport(1234)
}

Platform Support

Fully supported. Reads from /proc/[pid]/io. Includes both total I/O and disk-specific I/O counters.
On Linux, reading /proc/[pid]/io may require elevated privileges depending on the process owner and system configuration.

Understanding the Metrics

Read/Write Bytes vs Disk Read/Write Bytes

On Linux:
  • ReadBytes/WriteBytes: All I/O including cached operations
  • DiskReadBytes/DiskWriteBytes: Actual physical disk I/O
The difference represents I/O satisfied by the page cache:
cachedReads := io.ReadBytes - io.DiskReadBytes
cachedWrites := io.WriteBytes - io.DiskWriteBytes

CPU Usage

Monitor CPU usage

Memory Usage

Monitor memory usage

Process Info

Get process information

Overview

Back to process package overview

Build docs developers (and LLMs) love