/proc is the proc filesystem — one of Linux’s oldest virtual filesystems. It was originally designed to expose process information (hence the name), but has grown to expose a wide range of kernel and hardware state.
Unlike sysfs (/sys), which follows a strict object model, /proc is more of a grab-bag of useful information. Some entries are per-process, some are system-wide hardware info, and some are kernel tunables.
ls /proc/
# 1 2 3 ... (numbered directories = process PIDs)
# buddyinfo cmdline cpuinfo devices diskstats dma
# interrupts iomem ioports meminfo modules mounts
# net partitions slabinfo stat swaps sys uptime version
Each running process has a numbered directory:
ls /proc/1234/ # for PID 1234
# cmdline cwd environ exe fd maps mem net
# smaps stat status task ...
Key files per process:
cat /proc/$$/cmdline | tr '\0' ' ' # command line (null-separated)
cat /proc/$$/status # human-readable process info
cat /proc/$$/maps # memory mappings
ls -la /proc/$$/fd/ # open file descriptors
readlink /proc/$$/exe # path to executable
cat /proc/$$/environ | tr '\0' '\n' # environment variables
From Python:
import os
pid = os.getpid()
with open(f"/proc/{pid}/status") as f:
for line in f:
if line.startswith("VmRSS"):
print(line.strip()) # resident memory usage
Detailed information about each CPU core:
cat /proc/cpuinfo
processor : 0
vendor_id : GenuineIntel
cpu family : 6
model name : Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz
cpu MHz : 800.000
cache size : 8192 KB
physical id : 0
siblings : 8
core id : 0
flags : fpu vme de pse tsc msr pae mce cx8 apic ...
def cpu_info():
cores = []
current = {}
with open("/proc/cpuinfo") as f:
for line in f:
line = line.strip()
if line == "":
if current:
cores.append(current)
current = {}
elif ":" in line:
key, _, val = line.partition(":")
current[key.strip()] = val.strip()
return cores
cores = cpu_info()
print(f"CPU: {cores[0]['model name']}")
print(f"Cores: {len(cores)}")
System memory statistics:
cat /proc/meminfo
MemTotal: 16237440 kB
MemFree: 2134528 kB
MemAvailable: 8452096 kB
Buffers: 512000 kB
Cached: 5120000 kB
SwapTotal: 8388604 kB
SwapFree: 8388604 kB
def mem_info():
info = {}
with open("/proc/meminfo") as f:
for line in f:
key, _, val = line.partition(":")
info[key.strip()] = val.strip()
total = int(info["MemTotal"].split()[0])
avail = int(info["MemAvailable"].split()[0])
print(f"Memory: {avail//1024} MB free / {total//1024} MB total")
mem_info()
The interrupt table — shows which hardware is generating interrupts and how many:
cat /proc/interrupts
CPU0 CPU1 CPU2 CPU3
0: 18 0 0 0 IO-APIC 2-edge timer
1: 0 0 0 4213 IO-APIC 1-edge i8042
16: 0 0 0 0 IO-APIC 16-fasteoi i801_smbus
24: 0 12891 0 0 PCI-MSI 327680-edge xhci_hcd
25: 0 0 214760 0 PCI-MSI 360448-edge i915
NMI: 4 4 4 4 Non-maskable interrupts
LOC: 543219 578436 521847 509124 Local timer interrupts
Each row is an interrupt vector. The numbers show how many times each CPU has handled that interrupt. i915 is the GPU driver, xhci_hcd is USB.
def interrupt_counts():
with open("/proc/interrupts") as f:
header = f.readline().split() # CPU names
for line in f:
parts = line.split()
if len(parts) < 2:
continue
irq = parts[0].rstrip(":")
counts = parts[1:1+len(header)]
name = " ".join(parts[1+len(header):]) if len(parts) > 1+len(header) else ""
total = sum(int(c) for c in counts if c.isdigit())
if total > 0:
print(f"IRQ {irq:>4}: {total:>10} {name}")
The physical memory map — shows how physical addresses are allocated between hardware:
cat /proc/iomem
00000000-00000fff : Reserved
00001000-0009e7ff : System RAM
0009e800-0009ffff : Reserved
000a0000-000bffff : PCI Bus 0000:00
000c0000-000c7fff : Video ROM
...
00100000-7ef0bfff : System RAM
01000000-02006f42 : Kernel code
02006f43-027cbbff : Kernel data
80000000-8fffffff : PCI Bus 0000:01
80000000-8fffffff : 0000:01:00.0 ← GPU memory region
fd000000-fd3fffff : PCI Bus 0000:00
fd000000-fd0fffff : 0000:00:02.0 ← Intel GPU registers
This is crucial for memory-mapped I/O — the physical addresses where hardware registers live.
I/O port space (x86 only) — the legacy port-mapped I/O space:
cat /proc/ioports
0000-0cf7 : PCI Bus 0000:00
0000-001f : dma1
0020-0021 : PIC1
0040-0043 : timer0
0060-0060 : keyboard
0070-0071 : rtc_cmos
00f0-00ff : fpu
These are I/O port addresses (different from memory addresses) that legacy hardware uses. Modern hardware mostly uses MMIO instead.
Maps major device numbers to driver names (we saw this in Chapter 4):
cat /proc/devices
Currently loaded kernel modules (same as lsmod output):
cat /proc/modules | head -5
# e1000e 282624 0 - Live 0xffffffffc08e0000
Format: name size instances_loaded deps state address
cat /proc/mounts # all mounted filesystems
cat /proc/partitions # all disk partitions
Network statistics:
ls /proc/net/
# arp dev if_inet6 route tcp tcp6 udp unix ...
cat /proc/net/dev # per-interface packet/byte counters
cat /proc/net/arp # ARP table
cat /proc/net/route # routing table
/proc/sys/ contains writable kernel parameters (same as sysctl):
ls /proc/sys/
# kernel net vm fs ...
cat /proc/sys/kernel/hostname
# mycomputer
# Change hostname at runtime (does not persist reboot)
echo "newname" | sudo tee /proc/sys/kernel/hostname
# Equivalent sysctl command
sudo sysctl -w kernel.hostname=newname
Key tunables:
/proc/sys/net/ipv4/ip_forward # enable IP forwarding (router mode)
/proc/sys/vm/swappiness # swap aggressiveness (default 60)
/proc/sys/kernel/dmesg_restrict # who can read dmesg
/proc/sys/fs/file-max # max open file descriptors system-wide
These are used by tools like top, htop, and iostat:
cat /proc/stat # CPU time breakdown (user, system, idle, iowait...)
cat /proc/diskstats # disk I/O statistics per device
/proc/stat is what Python’s psutil library reads to compute CPU usage.
Previous: Chapter 5 — /sys