DiegoVallejo

Hardware as Memory, Software as Files: The Axioms of System Architecture

The Illusion of Abstraction

Modern development is inherently crippled by layers of opaque abstractions. To engineer performant and resilient systems, architecture must align with two empirical paradigms: at the silicon level, everything is memory; at the system level, everything is a file. Ignoring these facts leads to structural bloat and computational inefficiency.

1. The Silicon Reality: Everything is Memory

At the microarchitectural level, the CPU is blind to discrete peripherals. It operates on a single interactive model: load from an address, store to an address.

Through Memory-Mapped I/O (MMIO) and Direct Memory Access (DMA), interacting with a GPU or a network interface card is functionally identical to modifying RAM. Computing is strictly the mutation of state within a contiguous address map.

Engineering Implication: Data-Oriented Design

When the hardware reality is acknowledged, optimization shifts from theoretical object hierarchies to physical memory layout. Cache locality becomes paramount. Structuring data in contiguous arrays minimizes cache misses, often outperforming complex algorithms with scattered heap allocations. Furthermore, understanding DMA enables zero-copy architectures, passing memory pointers directly to network controllers and bypassing the CPU entirely.

2. Structural Composability: Everything is a File

The UNIX philosophy mitigates hardware topological complexity through a singular abstraction: the Virtual File System (VFS). Hardware devices, network sockets, and kernel states are exposed as file descriptors.

Engineering Implication: Agnostic Interfaces

This paradigm enforces extreme decoupling. Components must be designed to read and write byte streams, ignorant of the underlying source or destination. This enables pure pipeline architectures where standard outputs feed standard inputs seamlessly.

interface StreamNode {
    read(buffer: Uint8Array): number;
    write(buffer: Uint8Array): number;
    close(): void;
}

// O(N) linear time complexity for data transfer.
// O(1) space complexity. Prioritizing efficiency over readability.
class ZeroCopyRouter {
    route(input: StreamNode, output: StreamNode): void {
        const buffer = new Uint8Array(8192);
        let bytesRead: number;
        
        while ((bytesRead = input.read(buffer)) > 0) {
            output.write(buffer.subarray(0, bytesRead));
        }
    }
}

3. Bridging the Axioms: Hardware Interfacing in User Space

The practical implementation of "everything is a file" manifests predominantly in Linux virtual file systems like procfs and sysfs. In these environments, the kernel exports the internal hardware state as a file hierarchy.

Interacting with hardware does not require proprietary low-level libraries; standard I/O operations on specific file descriptors are sufficient. The following implementation extracts processor temperature and memory usage directly from mapped sensors.

import { readFileSync } from 'node:fs';

class HardwareMonitor {
  /**
   * @returns {number | null}
   */
  static getCpuTemperature() {
    try {
      const THERMAL_PATH = '/sys/class/thermal/thermal_zone0/temp';
      const raw = readFileSync(THERMAL_PATH, 'utf8');
      return parseInt(raw, 10) / 1000;
    } catch {
      return null;
    }
  }

  /**
   * @returns {{total: number, free: number, used: number} | null}
   */
  static getMemoryStatus() {
    try {
      const MEM_PATH = '/proc/meminfo';
      const data = readFileSync(MEM_PATH, 'utf8');
      
      let total = 0, free = 0, available = 0;
      const lines = data.split('\n');

      // O(N) where N is lines in meminfo. 
      // Key-based extraction is more resilient than index-based.
      for (let i = 0; i < lines.length; i++) {
        if (lines[i].startsWith('MemTotal:')) total = parseInt(lines[i].split(/\s+/)[1], 10);
        if (lines[i].startsWith('MemFree:')) free = parseInt(lines[i].split(/\s+/)[1], 10);
        if (lines[i].startsWith('MemAvailable:')) available = parseInt(lines[i].split(/\s+/)[1], 10);
        if (total && available) break; 
      }

      // MemAvailable is a more accurate metric for "free" RAM in modern Linux.
      return { total, free, used: total - (available || free) };
    } catch {
      return null;
    }
  }
}

const temp = HardwareMonitor.getCpuTemperature();
const mem = HardwareMonitor.getMemoryStatus();

if (temp !== null && mem !== null) {
  process.stdout.write(`CPU: ${temp}°C | RAM Used: ${(mem.used / 1024).toFixed(2)} MB\n`);
}

Operation Analysis

When the runtime executes readFileSync on /sys/class/thermal/thermal_zone0/temp, the execution flow validates both axioms:

  1. Syscall open(): The process requests a file descriptor from the kernel.
  2. VFS Routing: The kernel identifies the sysfs path and delegates the request to the corresponding thermal driver.
  3. Memory Mapping: The driver reads the hardware register (via MMIO) containing the thermal sensor's value.
  4. Syscall read(): The kernel formats the binary value from the hardware's memory into a text string and delivers it to the user space buffer.

This confirms that while the developer operates on a file, the underlying infrastructure is strictly moving bytes from a specific hardware memory address. There is no semantic difference between reading a text document and reading the state of a transistor.